Email Sending Through 10xDotIn
Use 10xDotIn email APIs to send handle-owned transactional or newsletter-style messages from a verified custom domain. The API stores the message, enqueues delivery, and exposes message status plus provider delivery events for troubleshooting.
Prerequisites
- A 10x handle where your user has
OPERATORor owner access. - A custom domain attached to the account.
- The domain email identity is
READY. - A sender profile using one of the approved local parts:
noreply,notifications,calendar,support, orhello. - Recipient consent for every recipient in the send request.
Endpoint Summary
| Job | Method and path | Auth |
|---|---|---|
| Check domain email identity | GET /v2/account/domains/{domain}/email-identity | JWT |
| Reconcile domain email identity | POST /v2/account/domains/{domain}/email-identity/reconcile | JWT |
| List sender profiles | GET /v2/handles/{handle}/email/senders | JWT |
| Create or update sender profile | PUT /v2/handles/{handle}/email/senders/{senderId} | JWT |
| List templates | GET /v2/handles/{handle}/email/templates | JWT |
| Create template | POST /v2/handles/{handle}/email/templates | JWT |
| Update template | PUT /v2/handles/{handle}/email/templates/{templateId} | JWT |
| Send message | POST /v2/handles/{handle}/email/messages | JWT |
| List messages | GET /v2/handles/{handle}/email/messages | JWT |
| Get message details | GET /v2/handles/{handle}/email/messages/{messageId} | JWT |
| List delivery events | GET /v2/handles/{handle}/email/events | JWT |
Basic Flow
Verify the domain email identity
Check the domain email identity and reconcile it until the identity reports
READY.Create a sender profile
Create a sender such as
support@example.comornotifications@example.com.Choose raw or template mode
Send raw
subjectplustextorhtml, or create a reusable template and send withtemplateId.Queue the message
Call
POST /v2/handles/{handle}/email/messageswith an idempotency key and consent attestation.Inspect status and events
Poll the message or event endpoints for
QUEUED,SENT, delivery, bounce, complaint, or failure state.
Auth Setup
Set these variables in your local shell:
export TENX_API_BASE="https://ai.10x.in"
export TENX_JWT="<jwt-token>"
export TENX_HANDLE="acme"
export TENX_DOMAIN="example.com"
Pass the JWT on every request:
-H "Authorization: Bearer ${TENX_JWT}"
Need help with JWTs? See API Auth and Error Model and JWT vs PAT: When to Use Each.
1. Verify Domain Email Identity
curl -sS "${TENX_API_BASE}/v2/account/domains/${TENX_DOMAIN}/email-identity" \
-H "Authorization: Bearer ${TENX_JWT}"
Expected shape:
{
"identity": {
"identityKey": "us-east-1#example.com",
"region": "us-east-1",
"domain": "example.com",
"status": "READY",
"requiredDnsRecords": [],
"mailFromDomain": "bounce.example.com"
},
"senderProfiles": []
}
If the status is not READY, reconcile after DNS changes:
curl -sS -X POST "${TENX_API_BASE}/v2/account/domains/${TENX_DOMAIN}/email-identity/reconcile" \
-H "Authorization: Bearer ${TENX_JWT}"
The reconcile endpoint returns 202 when the verification job is accepted. Re-check the identity after DNS propagation.
2. Create a Sender Profile
senderId must start with sender_. The localPart must be one of the approved values listed in prerequisites.
curl -sS -X PUT "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/senders/sender_support" \
-H "Authorization: Bearer ${TENX_JWT}" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"localPart": "support",
"displayName": "Acme Support",
"replyTo": "support@example.com",
"purposes": ["TRANSACTIONAL_API", "PRODUCT_NOTIFICATION"],
"enabled": true
}'
Response:
{
"sender": {
"senderId": "sender_support",
"domain": "example.com",
"localPart": "support",
"fromEmail": "support@example.com",
"displayName": "Acme Support",
"replyTo": "support@example.com",
"purposes": ["TRANSACTIONAL_API", "PRODUCT_NOTIFICATION"],
"enabled": true,
"status": "READY"
}
}
Valid purposes are:
| Purpose | Use it for |
|---|---|
TRANSACTIONAL_API | Receipts, account notices, workflow-triggered messages |
PRODUCT_NOTIFICATION | Product, lifecycle, and handle-owner notifications |
CALENDAR | Calendar-oriented messages or invites |
3. Send a Raw Message
Use raw mode when the integration supplies the subject and body directly.
curl -sS -X POST "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/messages" \
-H "Authorization: Bearer ${TENX_JWT}" \
-H "Content-Type: application/json" \
-d '{
"senderId": "sender_support",
"to": ["buyer@example.com"],
"purpose": "TRANSACTIONAL_API",
"idempotencyKey": "receipt-ord_12345",
"consentAttestation": true,
"subject": "Your receipt from Acme",
"html": "<p>Thanks for your order.</p>",
"text": "Thanks for your order."
}'
Accepted response:
{
"message": {
"messageId": "msg_abc123",
"handle": "acme",
"status": "QUEUED",
"purpose": "TRANSACTIONAL_API",
"senderId": "sender_support",
"recipientCount": 1,
"fromEmail": "Acme Support <support@example.com>",
"createdAt": "2026-05-30T08:00:00.000Z"
}
}
The endpoint returns 202 after the message is stored and queued. Delivery happens asynchronously.
4. Create and Use a Template
Create a reusable template when multiple messages share copy and layout.
curl -sS -X POST "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/templates" \
-H "Authorization: Bearer ${TENX_JWT}" \
-H "Content-Type: application/json" \
-d '{
"templateId": "tmpl_receipt",
"name": "Receipt",
"subject": "Receipt for {{orderId}}",
"html": "<p>Thanks, {{firstName}}. Your order {{orderId}} is confirmed.</p>",
"text": "Thanks, {{firstName}}. Your order {{orderId}} is confirmed."
}'
Then send with the template:
curl -sS -X POST "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/messages" \
-H "Authorization: Bearer ${TENX_JWT}" \
-H "Content-Type: application/json" \
-d '{
"senderId": "sender_support",
"to": ["buyer@example.com"],
"purpose": "TRANSACTIONAL_API",
"idempotencyKey": "receipt-ord_12346",
"consentAttestation": true,
"templateId": "tmpl_receipt",
"variables": {
"firstName": "Asha",
"orderId": "ord_12346"
}
}'
Template ids must start with tmpl_. If you omit templateVersion, the active template version is used.
5. Inspect Messages and Events
List recent messages:
curl -sS "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/messages?limit=25" \
-H "Authorization: Bearer ${TENX_JWT}"
limit defaults to 25 and is capped at 50. Use nextCursor from the response to fetch the next page:
curl -sS "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/messages?limit=25&cursor=${NEXT_CURSOR}" \
-H "Authorization: Bearer ${TENX_JWT}"
Get one message and its timeline:
curl -sS "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/messages/msg_abc123" \
-H "Authorization: Bearer ${TENX_JWT}"
List events for a message:
curl -sS "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/events?messageId=msg_abc123" \
-H "Authorization: Bearer ${TENX_JWT}"
List events by recipient:
curl -sS "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/events?recipient=buyer@example.com" \
-H "Authorization: Bearer ${TENX_JWT}"
List events by purpose and time window:
curl -sS "${TENX_API_BASE}/v2/handles/${TENX_HANDLE}/email/events?purpose=TRANSACTIONAL_API&since=2026-05-30T00:00:00.000Z" \
-H "Authorization: Bearer ${TENX_JWT}"
Idempotency, Limits, and Consent
idempotencyKeyis required for every send. Reusing the same key for the same handle returns409 duplicate_idempotency_key.toaccepts up to 10 unique email addresses per request.consentAttestationmust betrue. If it is missing or false, the API returns400 consent_attestation_required.- Raw sends require
subjectand at least one ofhtmlortext. - Template sends require an active template with the requested
templateIdand optionaltemplateVersion. - Messages are subject to quota and suppression checks before they are queued.
Common Errors
| Error | Meaning | Fix |
|---|---|---|
domain_email_identity_not_ready | Domain is not ready to send mail | Check DNS records and run identity reconcile |
sender_profile_not_found | senderId does not exist on this handle | Create or correct the sender profile |
sender_profile_not_ready | Sender is disabled or not ready | Enable the sender or choose a ready sender |
sender_purpose_not_allowed | Sender purpose allowlist does not include the requested purpose | Update sender purposes or change the message purpose |
email_recipient_suppressed | Recipient is blocked by tenant suppression state | Do not retry until suppression is resolved |
email_quota_exceeded | Daily or per-minute quota is exhausted | Wait for quota reset or reduce volume |
duplicate_idempotency_key | The idempotency key has already been used | Reuse only for safe retry checks; use a new key for a new message |
invalid_email_content | Raw send is missing subject or body | Provide subject and text or html |
email_template_not_found | The requested template does not exist | Create the template or correct templateId |
transactional_email_queue_unavailable | Delivery queue is unavailable in the environment | Retry later or contact support |
Recommended Integration Pattern
- Reconcile and verify the domain before enabling sending in your app.
- Create one sender profile per sender role, for example
sender_supportandsender_notifications. - Use templates for repeated lifecycle messages.
- Generate stable idempotency keys from your own business object ids, such as
receipt-${orderId}. - Treat
202 QUEUEDas accepted for delivery, not proof of inbox delivery. - Store
messageIdand inspect events when debugging bounces, complaints, or provider failures. - Use tracked 10x links in email CTAs when you need click and conversion attribution.
Updated Jun 19, 2026
