Requests and errors
Base URL and format
All endpoints are under https://api.sealreply.com/v1. Send and receive JSON (Content-Type: application/json). Times are ISO 8601 in UTC. IDs are UUIDs; message IDs sort by time.
Every response is an envelope:
{ "success": true, "data": { … }, "error": null }Errors
On failure success is false, data is null, and error explains:
{
"success": false,
"data": null,
"error": {
"code": "validation_failed",
"message": "Some fields are invalid. Please check and try again.",
"fields": { "email": "invalid" },
"request_id": "req_01a1…"
}
}Program against code; message is for people and follows the Accept-Language header. Quote request_id when you contact support.
| Code | Status | Meaning |
|---|---|---|
bad_request | 400 | The request could not be read. |
validation_failed | 422 | A field is invalid; fields names it and why. |
unauthorized | 401 | Missing, unknown or revoked key. |
forbidden | 403 | The key lacks the scope (fields.scope), or the endpoint is not open to keys. |
tenant_suspended | 403 | The workspace is suspended. |
not_found | 404 | No such resource in your workspace. |
conflict | 409 | Changed meanwhile, or the same Idempotency-Key is still being handled. |
conversation_closed | 409 | The conversation has ended. |
rate_limited | 429 | Too many requests; wait Retry-After seconds. |
quota_exceeded | 402 | A plan limit is reached; fields.limit says which. |
plan_upgrade_required | 402 | Your plan lacks the feature; fields.feature says which. |
internal | 500 | Our fault. Safe to retry. |
Pagination
Lists return up to limit items (default 20, at most 100) and a cursor:
{ "data": [ … ], "meta": { "has_more": true, "next_cursor": "MjAyNi0…" } }Pass cursor=<next_cursor> for the next page until has_more is false. Conversation lists are newest activity first; message history pages backwards from the newest.
Retrying safely
- Messages: give every message a
client_msg_idyou generate (a UUID). Sending again with the same one returns the first message instead of a duplicate. - Any write: send an
Idempotency-Keyheader (up to 255 characters). For 24 hours, repeating the same request returns the first successful response withIdempotent-Replayed: true. Reusing a key for a different body returns422withfields.Idempotency-Key = reused; a repeat while the first is still running returns409 conflict. Failed responses are not stored, so you can retry them.
Unknown fields
Responses and webhook bodies may gain new fields and new enum values over time. Ignore the ones you do not know rather than failing on them.