API reference
API introduction
Use your installation’s HTTP API with bearer authentication.
Base URL
Set OPENSEND_BASE_URL to the public Convex HTTP origin for your installation, such as https://api.example.com. It is not the dashboard origin or the Convex query/WebSocket origin. Do not append /api. There is no shared cloud endpoint.
Authentication
Send Authorization: Bearer os_… using an API key from the dashboard or POST /api-keys. Full-access keys manage the shared REST resources. OAuth grant management accepts full-access API keys and OAuth tokens through the same REST wrapper. Sending keys reach email send and batch endpoints, and can be restricted to one domain. OAuth full_access and emails:send scopes map to those permissions. SMTP accepts API keys only.
Errors
Shared REST errors use JSON with statusCode, name, and message. The OAuth grant routes use the same errors, rate headers, request logging, and cursor pagination.
| Status | Names and meaning |
|---|---|
| 400 | invalid_idempotency_key; validation_error for invalid JSON or multipart data |
| 401 | missing_api_key; restricted_api_key for a sending key on a full-access endpoint |
| 403 | invalid_api_key; invalid_permission for an OAuth token without the needed scope; restricted_api_key for a sending key whose domain was removed; validation_error when creating a domain another team registered |
| 404 | not_found for an unknown or foreign resource |
| 409 | invalid_idempotent_request, concurrent_idempotent_requests |
| 413 | validation_error: request body too large |
| 422 | validation_error, missing_required_field, invalid_attachment |
| 429 | rate_limit_exceeded |
| 500 | application_error |
{"statusCode":401,"name":"missing_api_key","message":"Missing API key in the authorization header."}Rate limits
All keys share 10 requests per second per team. Rate-limited responses include retry-after in seconds. Responses that pass the shared request wrapper include ratelimit-limit, ratelimit-remaining, and ratelimit-reset (seconds until the window resets, not a timestamp). Early authentication or body-size failures can omit those headers.
Idempotency
Any POST accepts Idempotency-Key, 1–256 characters. The same key with the same method, path, and exact JSON body replays the stored response for 24 hours. A different request returns 409. Resource creation and the stored response commit in one transaction. Matching concurrent requests return 409 concurrent_idempotent_requests. An uncommitted reservation can be retried after 60 seconds, with stale workers fenced before writing. Server errors release only uncommitted reservations; crashes or logging failures cannot erase a committed response. See idempotency keys. Multipart contact imports hash sorted decoded fields and CSV contents, so new form boundaries do not change the request identity. This protects HTTP submission; it cannot guarantee exactly-once delivery across an ambiguous SES timeout.
Pagination
Use limit from 1 to 100 (default 20) and either after or before, not both. Lists are newest first and return object, has_more, and data. Use the last row’s ID as after for the next page. Invalid or foreign anchors return 422. All audience lists are paginated even where Resend may return every row by default. Webhook event and attempt lists accept limit and after only; before returns 422.
Compatibility boundaries
- IDs are Convex document IDs, not UUIDs.
- The default JSON body limit is 1 MiB. Single-email sending permits a larger body; deployment and proxy caps still apply.
- There are no CORS headers. Call from your server.
- Single sends accept base64 attachments and public HTTPS attachment URLs. HTTP/private URLs and batch attachments are rejected.
- REST accepts HTML/text, not React elements. SDKs render React before sending.
- Templates have one mutable draft and one published snapshot, without history endpoints.
- Automations support custom event triggers and the eight documented step types through the dashboard and REST API.
- Suppression and webhook management, contact imports, metrics, OAuth grants, email sharing, domain claims, and usage have public REST endpoints.
- Domain creation accepts TLS and sending/receiving capabilities, subject to SES readiness and regional support.
Endpoint pages are generated from the app’s OpenAPI 3.1 specification: 113 public operations, with the two SMTP gateway operations excluded. Read Compatibility with Resend and the operation-specific notes before migrating a client.