UniOne FastAPI 1.1.3
Web API 1.90
Quickstart guide: Send emails with Python · Docs for LLMs: llms.txt · API reference
FastAPI integration of the UniOne Python SDK (``): the API client in the dependencies (Depends(get_client)), a webhook endpoint with signature check.
Install
pip install unione-fastapi
Requirements: Python 3.8+, FastAPI 0.100+.
Get an API key
- Sign up at cp.unione.io.
- Copy your API key from Settings → Security → API.
- Put it in an environment variable:
export UNIONE_API_KEY="your-api-key"
The package reads it from UNIONE_API_KEY (see the configuration below).
Server-side only. Use this package in backend code: API routes, server functions, workers. Never ship the API key to a browser or a mobile app, and never commit it.
Configuration
Settings are read from the environment by default:
UNIONE_API_KEY=YOUR_API_KEY
UNIONE_HOST=us1.unione.io
| Option | Environment variable | Default | Description |
|---|---|---|---|
Settings.api_key |
UNIONE_API_KEY |
User or project API key (required). | |
Settings.host |
UNIONE_HOST |
api.unione.io |
API host (datacenter: api.unione.io, eu1.unione.io, us1.unione.io) or a full base URL. |
Settings.timeout |
UNIONE_TIMEOUT |
30 |
Request timeout, seconds. |
Settings.webhook_key |
UNIONE_WEBHOOK_KEY |
API key the webhooks are signed with (the key of the user/project of the webhook), if it differs from the API key. | |
Settings.webhook_path |
UNIONE_WEBHOOK_PATH |
/unione/webhook |
Path of the webhook endpoint. |
Other settings in code: app.dependency_overrides[get_settings] = lambda: Settings(api_key=...).
Send your first email — no DNS setup
Every account can send test email right away from a sandbox domain: pre-verified, DKIM and SPF already set. It delivers only to addresses you confirm.
The examples use the API client of the package (see the configuration above).
1. Get your sandbox domain (domain/get-sandbox-domain). The call creates the domain on first use and returns the same one afterwards.
from fastapi import Depends
from unione import UniOneClient
from unione_wrapper import get_client
@app.post("/sandbox-domain")
def get_sandbox_domain(client: UniOneClient = Depends(get_client)):
response = client.domain.get_sandbox_domain()
return {"domain": response.domain}
2. Confirm your inbox (confirmed-email/create). UniOne sends a confirmation email; click the link in it within 48 hours.
from fastapi import Depends
from unione import UniOneClient
from unione_wrapper import get_client
from unione.api.confirmed_email import ConfirmedEmailRequest
@app.post("/confirm-email")
def confirm_email(client: UniOneClient = Depends(get_client)):
response = client.confirmed_email.create(ConfirmedEmailRequest(
email="you@example.com",
))
return {"status": response.status}
Check the status with confirmed-email/list — your address should show "status": "confirmed".
3. Send the email (email/send) from any address on the sandbox domain to the confirmed address.
from fastapi import Depends
from unione import UniOneClient
from unione_wrapper import get_client
from unione.api.email import EmailSendRequest, MessageObject, MessageObjectRecipientsItem
from unione.common import BodyObject
@app.post("/send")
def send(client: UniOneClient = Depends(get_client)):
response = client.email.send(EmailSendRequest(
message=MessageObject(
recipients=[
MessageObjectRecipientsItem(
email="you@example.com",
),
],
body=BodyObject(
html="<p>It works!</p>",
),
subject="Hello from UniOne",
from_email="noreply@<sandbox-domain>",
),
))
return {"job_id": response.job_id}
What you get back
{
"status": "success",
"job_id": "1ZymBc-00041N-9X",
"emails": [
"you@example.com"
]
}
On error the API returns status: "error", a message and a numeric code:
{
"status": "error",
"code": 102,
"message": "Error ID:83274F98-C7D5-11F1-B221-6214FF8707B1. Can not decode key wrongkey"
}
Same flow with curl
BASE=https://api.unione.io/en/transactional/api/v1
curl -s -X POST $BASE/domain/get-sandbox-domain.json \
-H "Content-Type: application/json" -H "X-API-KEY: $UNIONE_API_KEY" -d '{}'
curl -s -X POST $BASE/confirmed-email/create.json \
-H "Content-Type: application/json" -H "X-API-KEY: $UNIONE_API_KEY" \
-d '{"email": "you@example.com"}'
curl -s -X POST $BASE/email/send.json \
-H "Content-Type: application/json" -H "X-API-KEY: $UNIONE_API_KEY" \
-d '{
"message": {
"recipients": [
{
"email": "you@example.com"
}
],
"from_email": "noreply@<sandbox-domain>",
"subject": "Hello from UniOne",
"body": {
"html": "<p>It works!</p>"
}
}
}'
Webhooks
The endpoint answers GET with 200 OK (the URL check of webhook/set) and accepts the POST notifications: it checks the auth field (MD5 of the request body in which the auth value is replaced by the API key), answers 401 to a wrong signature, and passes the notification and each of its events to the application.
from unione_wrapper import Webhooks
webhooks = Webhooks()
@webhooks.on_event
async def on_event(event, user):
data = event.event_data
print(event.event_name, data.email, data.status)
app.include_router(webhooks.router()) # /unione/webhook
Register the URL of the endpoint with webhook/set, e.g. https://example.com/unione/webhook.
API client
Every section of the API is available on the client, see the SDK docs.
from fastapi import Depends, FastAPI
from unione import UniOneClient
from unione_wrapper import get_client
app = FastAPI()
@app.get("/usage")
def usage(client: UniOneClient = Depends(get_client)): # the client is synchronous: a def endpoint
info = client.system.info()
return {"emails_sent": info.accounting.emails_sent if info.accounting else None}
Sending through the API
The client calls email/send with all of its options: templates, substitutions, metadata, tracking. Fields of the request and other examples: Python SDK docs.
from fastapi import Depends
from unione import UniOneClient
from unione_wrapper import get_client
from unione.api.email import EmailSendRequest, MessageObject, MessageObjectRecipientsItem
from unione.common import BodyObject
@app.post("/send")
def send(client: UniOneClient = Depends(get_client)):
response = client.email.send(EmailSendRequest(
message=MessageObject(
recipients=[
MessageObjectRecipientsItem(
email="user@example.com",
substitutions={
"CustomerId": "12452",
"to_name": "John Smith",
},
),
],
body=BodyObject(
html="<b>Hello, {{to_name}}</b>",
plaintext="Hello, {{to_name}}",
),
subject="UniOne test email ",
from_email="email@example.com",
from_name="John Smith",
),
))
return {"job_id": response.job_id}
Sandbox limits
- Sends only to confirmed addresses: up to 10 per account.
- Up to 100 emails a day.
- The sandbox domain expires after 31 days; call
domain/get-sandbox-domainagain to get a new one. - Works only with the account API key, not a project key.
- Not listed in
domain/list; the otherdomain/*methods do not apply to it.
The sandbox is for development and testing. Do not use a sandbox domain as the sender in production.
Before production: verify your domain
-
domain/get-dns-records— returns the values for your DNS records. -
Add these records at your DNS provider (the domain page of the control panel shows them too):
Type Host Value TXT @the verification-recordvalue (unione-validate-hash=…)TXT <selector>._domainkey(the selector is on the domain page of the control panel, e.g.us)v=DKIM1; k=rsa; p=<dkim>TXT @v=spf1 include:spf.unione.io ~allCNAME _dmarcthe dmarcvalue -
domain/validate-verification-record, thendomain/validate-dkim. -
domain/list— wait until the verification record is confirmed and DKIM is active. -
Read
from_emailfrom your config, so switching from the sandbox needs no code change.
Sending reliably
- Retries: set
message.idempotence_keyon sends that may be retried, so a repeated request does not send twice. - Limits: up to 500 recipients and 10 MB per request; each attachment up to 7 MB.
- Body: always include
body.htmlorbody.plaintext. - Delivery status: register a webhook (
webhook/set) with a fast, idempotent HTTPS handler instead of polling. - Defaults: keep the default unsubscribe footer and tracking; changing them requires UniOne support.
- Errors: retry
429and5xxwith backoff; do not retry other errors unchanged.
Links
- Send emails with Python and other integration guides
- Web API reference
- llms.txt for AI coding agents
- UniOne MCP server and agent setup
- Support
For package contributors
See AGENTS.md: how this package is generated from the OpenAPI spec, built, tested and released.
Metadata
Release files for unione-fastapi 1.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| unione_fastapi-1.1.3.tar.gz | 9.1 kB | With provenance | Details for unione_fastapi-1.1.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| unione_fastapi-1.1.3-py3-none-any.whl | Python 3 | none | any | Details for unione_fastapi-1.1.3-py3-none-any.whl |
Total release size: 19.4 kB
Release files / unione_fastapi-1.1.3.tar.gz
| Download URL | unione_fastapi-1.1.3.tar.gz |
|---|---|
| Size | 9.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c881c5aca3720074ab595da324425fd18033fcf613f2e1480ba2bd3f14b8a8c7
|
|
BLAKE2b-256 checksum How to use checksums |
2b09dc00fb07143ebb5b991a827dca15af81825f940d2f5ccedecce3b6ba8216
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / unione_fastapi-1.1.3-py3-none-any.whl
| Download URL | unione_fastapi-1.1.3-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1d7ca5be799450584771febf52d0951f6149866c3bcc14d73d81ff136f17bcd7
|
|
BLAKE2b-256 checksum How to use checksums |
40763b350bd35dd6a04a2660c9db75abb11e9e3376b52bd45f6bfeddceaac4e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for unione-fastapi 1.1.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for unione_fastapi-1.1.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.