Skip to main content
AhaSend lets you send transactional email from Google Cloud Run functions in Python through the HTTP API; this guide adds a protected function and runs it locally. Cloud Run functions is the current name for the service previously called Cloud Functions.

Prerequisites

Use a verified sending domain, your account ID, and a domain-scoped send-only API v2 key. Use sandbox mode for every test. Management tasks need a separate full API key. Use Python 3.13 and the Python Functions Framework. You can complete the local test without a Google Cloud account.

Install the Dependencies

Try a Sandbox Send

Set AHASEND_API_KEY, AHASEND_ACCOUNT_ID and AHASEND_FROM in your shell or local secret store. Use a send-only key and an address on your verified domain. Save this as quick_send.py:
quick_send.py
Run python quick_send.py in the activated environment. It prints acceptance statuses and exits with an error if sending fails. Sandbox acceptance does not deliver mail. The production version below adds input checks and a stable request key.

Add the Shared Sending Code

Save this as send_email.py beside the function file. It uses Python’s standard library, so this example needs no SDK package. Set AHASEND_API_KEY, AHASEND_ACCOUNT_ID, AHASEND_FROM, AHASEND_TO and SEND_TOKEN in the runtime environment. Use a long random SEND_TOKEN for requests from your trusted server. For the first run set AHASEND_TO=recipient@example.com and AHASEND_DELIVERY_MODE=sandbox; keep a sandbox credential in place while testing. Set live only when ready to deliver to your own approved recipients.
send_email.py
Only your server chooses the sender, recipient and message body. Look up a user’s verified address from your database before adapting this to a real signup flow. Keep that saved message unchanged when retrying an event_id; a new business event needs a new ID. See idempotency for time limits and uncertain outcomes. Record an internal event ID on failures without logging API keys or message bodies. Add request limits at the hosting layer.

Create the HTTP Function

Save this as main.py:
main.py

Run with the Functions Framework

If port 8080 is busy, set PORT to an unused local port in both shells. The commands below default to 8080. Export the shared code’s environment variables, activate .venv, then start the function:
In another shell with SEND_TOKEN set, run:
Expect HTTP 202 with acceptance statuses. A wrong token returns 401, invalid input returns 400 and a failed upstream request returns 502. Use a new event ID for each new test and keep the original one when retrying the same job.

Prepare the Hosted Function

Deploy with the Python 3.13 runtime and entry point send_email_http. Include main.py, send_email.py and requirements.txt. Follow Google’s function deployment steps, load credentials with Secret Manager, and restrict invokers with IAM. With both IAM and the application bearer token enabled, send the Google identity token in X-Serverless-Authorization and the app token in Authorization; see service authentication. Use delivery webhooks to learn the final result. Also see Google functions in Node.js, Go and Python SMTP.