Documentation menu

API reference

API reference

tsmartSend is a JSON REST API at https://send.tsmart.ai. This section describes every endpoint; this page covers what they all share.

Base URL

All endpoints are relative to:

https://send.tsmart.ai

GET https://send.tsmart.ai/ answers without a key and returns a short description of the API with its list of resources; every other endpoint needs an API key.

Authentication

Send your API key as a bearer token in the Authorization header. Keys look like ts_ followed by an 8-character id, an underscore and a 32-character secret, for example ts_8Kq2Lm7P_…. A key is shown once, when it is created; only a hash is stored.

cURL
curl https://send.tsmart.ai/domains \
  -H "Authorization: Bearer $TSMART_API_KEY"

Create keys in the company console under tsmartSend → API keys, or with POST /api-keys. See API keys for how to store and rotate them.

Permissions

Every key has one of two permissions:

  • sending_access can only send: POST /emails and POST /emails/batch. It can be restricted to one domain, in which case from must use that domain.
  • full_access can call every endpoint: sending, reading emails and logs, and managing domains, keys, webhooks, suppressions, and the company's mailboxes and groups.

A full-access key acts with the console rights of the person who created it, checked on every call: it can never do more through the API than that person may do in the console, and it loses those rights when the person does. Sending needs no privilege, but every key works only while its creator still has access to the company: removing an admin revokes the keys they created (see API keys).

Endpoints Key permission Key creator needs (console)
POST /emails, POST /emails/batch any key nothing
Other /emails endpoints, GET /domains, GET /domains/{id}, /api-keys, /webhooks, /suppressions, /logs full_access the API controls privilege
POST /domains, PATCH /domains/{id}, POST /domains/{id}/verify full_access the Domains privilege
DELETE /domains/{id} full_access to be the company owner
/mailboxes, /groups full_access the privilege of each change: View, Create, Update or Suspend users, Reset user passwords, View or Manage groups (see Mailboxes); deleting a mailbox or a group needs the company owner

A sending-access key that calls a full-access endpoint gets 401 restricted_api_key. A full-access key whose creator lacks the right gets 403 invalid_permission.

Requests and responses

Send request bodies as JSON with Content-Type: application/json. Field names are snake_case. Every response from the API, including errors, is JSON.

The JSON body of most endpoints may be at most 1 MB (64 KB for /mailboxes and /groups); a larger one is refused with 413 payload_too_large. The body of POST /emails and POST /emails/batch may be at most 30 MB, enough for the 20 MB of attachments an email may carry (base64 encoding makes a file about a third larger).

A body over 30 MB, on any endpoint, is refused before the request reaches the API. That answer is a 413 without a JSON body.

Errors

Errors use the HTTP status code and always have the same body: the status code, a stable machine-readable name and a human-readable message. Match on name; the message text may change.

422 Unprocessable Entity
{
  "statusCode": 422,
  "name": "validation_error",
  "message": "`to` is required"
}
Status Name Meaning
400invalid_idempotency_keyThe Idempotency-Key header is not 1 to 256 characters long.
401missing_api_keyNo Authorization: Bearer … header.
401restricted_api_keyA sending-access key called an endpoint that needs full access.
403invalid_api_keyThe key is malformed, unknown or revoked, or the company is suspended.
403service_disabledtsmartSend is turned off for the company.
403account_suspendedThe company has an overdue invoice: the service is suspended until it is paid. The message names the invoice and the payment page; paying restores access at once.
403ip_not_allowedThe company allows API calls only from listed addresses, and this one is not listed.
403invalid_permissionThe person who created the key may not do this in the console.
403invalid_from_addressThe from domain is not one of the company's domains with proven ownership, or the key is restricted to another domain.
403domain_not_verifiedThe from domain's SPF and DKIM records were not found at its last check. Publish them and call POST /domains/{id}/verify.
403account_inactiveThe company's subscription is not active.
404not_foundUnknown path, or the object does not exist in your company.
405method_not_allowedThe path exists but not with this HTTP method.
409invalid_idempotent_requestThe idempotency key was used in the last 24 hours with a different body.
409concurrent_idempotent_requestsA request with the same idempotency key is still running.
409resource_in_useThe domain still has mailboxes and cannot be deleted.
409already_existsThe address of a new mailbox, alternate email or group is already used by a mailbox, a group or an alternate email.
413payload_too_largeThe request body is over the endpoint's 1 MB limit (see Requests and responses).
422missing_required_fieldA required field is missing.
422 (403 or 429 in some cases on POST /domains, POST /webhooks and POST /mailboxes)validation_errorA field, query parameter or header has an invalid value, or the body is not valid JSON. The message says which. POST /domains answers 403 for a domain that cannot be added and 429 when too many domains wait for verification; POST /webhooks answers 403 at the webhook limit; POST /mailboxes answers 403 at the plan's user limit.
422invalid_attachmentAn attachment is malformed, or uses a path or URL instead of base64 content, or the attachments together exceed 20 MB after decoding ("Attachments exceed 20 MB").
429rate_limit_exceededToo many requests per second, or a sending limit was reached; also a metrics request of the company that is already running, or too many waiting webhook test events.
429monthly_quota_exceededThe plan's monthly email allowance is used up.
500application_errorAn unexpected error on our side. Details are never returned.
502mail_server_errorThe mail server refused a mailbox, alternate email or group change. The message carries only its error codes.

Every authenticated endpoint can return the authentication errors (missing_api_key, invalid_api_key, service_disabled, account_suspended, ip_not_allowed), the per-second rate_limit_exceeded and application_error. The reference pages list the other errors of each endpoint.

Pagination

List endpoints return a page of objects with cursor pagination:

Name Type Description
limit integer Number of objects to return, 1 to 100. Default 20.
after string Id of an object from a previous page: return the objects that come after it in the list order.
before string Id of an object from a previous page: return the objects that come before it in the list order. Use either after or before, not both.
200 OK
{
  "object": "list",
  "has_more": true,
  "data": [ … ]
}

Most lists are ordered newest first, so to read the next (older) page pass the id of the last object as after. has_more is true when more objects exist beyond this page in the direction you are paging. A cursor must be the id of an object in the same list; anything else gives 422 validation_error ("Invalid pagination cursor"). Mailboxes and groups are ordered by address, and their cursors are addresses.

cURL
curl "https://send.tsmart.ai/emails?limit=50&after=4ef9a417-02e9-4d39-ad75-9611e0fa6f60" \
  -H "Authorization: Bearer $TSMART_API_KEY"

Idempotency

POST /emails and POST /emails/batch accept an Idempotency-Key header (1 to 256 characters) so a retried request never sends twice. Keys are kept for 24 hours, separately per company and endpoint.

  • A retry with the same key and the same body gets the stored response, with the header idempotent-replayed: true, and nothing is sent again.
  • The same key with a different body is refused with 409 invalid_idempotent_request.
  • A retry while the first request is still running is refused with 409 concurrent_idempotent_requests.
  • A request that fails (any status outside 2xx) releases its key, so it can be retried with the same key.
cURL
curl -X POST https://send.tsmart.ai/emails \
  -H "Authorization: Bearer $TSMART_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: welcome-user/123" \
  -d '{"from":"Acme <hello@yourdomain.com>","to":"jane@example.com","subject":"Welcome","text":"Hello!"}'

Use a key that identifies the business event (an order number, a user id and a template name), not a random value per attempt.

Rate limits

Each company may make 10 requests per second across all of its keys (a sliding one-second window). Every authenticated response carries the current state, except a restricted_api_key error, which still counts against the rate:

Header Value
ratelimit-limitRequests allowed per window (10).
ratelimit-remainingRequests left in the current window.
ratelimit-resetSeconds until the window resets (always 1).
retry-afterOnly on a 429 for the request rate or for requests in progress: seconds to wait before retrying.

Sending is also limited by recipients (each address in to, cc and bcc counts): 450 per company in any hour (a fair share of the platform's capacity), at most 20,000 recipients waiting in scheduled emails, and the monthly allowance of your plan. These answer 429 rate_limit_exceeded or 429 monthly_quota_exceeded without retry-after. A company may also have at most 25 requests in progress, and one request body over 2 MB at a time; these answer 429 rate_limit_exceeded with retry-after. See Limits and errors.

A shared platform-wide hourly cap can also answer 429 rate_limit_exceeded ("Hourly sending limit reached") before your own limits are reached; retry later.

Timestamps and ids

Timestamps in API objects are UTC with microseconds, in the form 2026-10-12 09:15:02.481233+00. Webhook payloads, webhook delivery attempts (sent_at), the created_at of GET /webhooks/{id} and the range of GET /emails/metrics use ISO 8601 with a Z, for example 2026-10-12T09:15:02.481Z. Inputs such as scheduled_at take ISO 8601 with a time zone.

Emails, domains, API keys, webhooks, suppressions and log entries have UUID ids. Email events have numeric string ids; webhook events have ids that start with msg_. Mailboxes, alternate emails and groups are named by their address, URL-encoded in the path.

Resources

The Node.js SDK, the Python SDK and the CLI cover every endpoint; each reference page lists the matching method and command.