Bubble has no SMTP setting. Its only email option, under Settings → Domain / email, is a SendGrid API key — leave it empty and messages go out from a bubbleapps.io no-reply address. To send from your own domain through Postwing, call the REST API with Bubble's API Connector plugin: one POST call, set up once and used as an action in any workflow.
Bubble does not speak SMTP, so the connection settings you would use elsewhere do not apply here. The API authenticates with the same domain token — its login and password travel in the auth object of the request body:
POST https://api.postwing.app/external/send_email_simple/ The sender must be on your verified domain and written with a display name, Acme <noreply@your-domain.com>. An address on any other domain is refused: its DKIM and SPF are not yours, so it would fail DMARC at the recipient anyway. The full field list is in the API reference.
Open Plugins → Add plugins, find API Connector (published by Bubble) and install it. Then, in the plugin's settings, add an API — call it Mail — with Authentication set to None or self-handled. The credentials go in the body, so Bubble has nothing to handle.
Add a call to that API and fill it in:
| Field | Value |
|---|---|
| Name | Send email |
| Use as | Action — so it can run from a workflow |
| Data type | JSON |
| Method and URL | POST https://api.postwing.app/external/send_email_simple/ |
| Header | Content-Type: application/json |
| Body type | JSON |
Paste the body below. Every name in angle brackets becomes a parameter that Bubble lists under the body:
{
"to": [<to>],
"subject": <subject>,
"body": <html>,
"sender": "Acme <noreply@your-domain.com>",
"auth": {
"username": "<token_login>",
"password": "<token_password>"
}
}| Parameter | Private | Value |
|---|---|---|
token_login | Checked | The login of an SMTP token for your domain |
token_password | Checked | That token's password |
to | Unchecked | Test value "you@your-domain.com", with the quotes |
subject | Unchecked | "Test from Bubble" |
html | Unchecked | "<p>It works.</p>" |
to, subject and html are filled from workflows with :formatted as JSON-safe, which escapes quotes and line breaks and wraps the value in quotes itself. That is why the body leaves them bare and the test values carry their own quotes. The token parameters are fixed strings, so they stay quoted in the body. Tick Include errors in response and allow workflow actions to continue if you want to react to a refused send in the workflow instead of having it stop.
Click Initialize call. Bubble sends a real request — the test message arrives at the address you gave — and shows the response it will expose to workflows:
{
"ok": true,
"emails": [
{
"ok": true,
"message_id": "<uuid@your-domain.com>",
"uuid": "uuid",
"recipient": "customer@example.com",
"kind": "to",
"replayed": false
}
]
}If the response is an error instead, Bubble shows its text; the table below says what each one means.
In any workflow, add the action named after the API and the call — Mail - Send email — and fill its three inputs with dynamic expressions, each ending in :formatted as JSON-safe: for example Current User's email, a subject built from the order number, and the HTML of the message.
API Connector actions run on Bubble's server, and private parameters never leave it. What a page workflow can decide is the recipient, so do not wire the action to a form where a visitor types any address: that turns your domain into a way to mail strangers, and the complaints land on your reputation. Send to the current user, to your own team, or from backend workflows.
| Error | Cause and fix |
|---|---|
Invalid username or password | Wrong token login or password in the private parameters, or the token was deleted. |
Invalid sender, must end with @your-domain.com | The sender is not on the token's domain. |
Required format: full name <username@domain.ltd> | Add a display name before the address in sender. |
| 400 with a JSON parse error | A dynamic value is not formatted as JSON-safe, or the body quotes a parameter that JSON-safe already quotes. |
Either `recipient` or `to` is required | The recipient expression evaluated to nothing. |
| A parameter cannot be set in the workflow | It is marked Private. Only the token parameters should be. |
Hourly limit exceeded | Your plan's volume. It clears on its own; retry later. |
No. Settings → Domain / email takes a SendGrid API key and nothing else; without one, Bubble sends from a bubbleapps.io no-reply address. To send from your own domain through another provider, call its HTTP API with the API Connector, which is what this guide does.
No — that action always uses Bubble's own sending, or SendGrid if you added a key. Replace it in your workflows with the API Connector action set up here; the inputs are the same three things: recipient, subject and body.
Yes, as long as its parameter is marked Private. Private values stay on Bubble's server and never reach the visitor's browser. A parameter that is not private can be set from a workflow, which is exactly why the token must never be one of them.
The value was inserted into the JSON raw. End every dynamic expression with :formatted as JSON-safe, and do not put quotes around the <parameter> in the body — the operator adds them itself, and quoting twice produces invalid JSON too.
Change the body to "to": <to> (without the brackets) and pass a list of texts formatted as JSON-safe, which Bubble writes as a JSON array. Every address becomes its own message with its own delivery status.
Yes. Create a second call to /external/send_email_tpl/ with recipient, tpl (the template slug), lang and a params object, and keep the HTML in the dashboard rather than in your Bubble editor.
Initialization sends a real request so Bubble can read the response. Use your own address as the test value for the recipient.