Guides
Errors
How each API reports a failed request, with an example of every error it can return.
Each API reports errors in its own envelope. The envelopes differ, so each is described as it is. Branch on the status and the code, never on the message text. Every code, its message, and what to do about it are in the Error reference.
Dynamic Audience API
A failed request returns a top-level { "code", "message" } body with its HTTP status. Each status means the whole request failed and nothing was mailed or charged. A request that succeeded returns 200, and each recipient that was not mailed carries its own reason instead (Reasons a recipient was not mailed).
504) or a dropped connection may come after some recipients were mailed. Send the same payload again: no one is mailed twice. Within 15 minutes of a request that finished, you get its stored response; otherwise recipients already mailed come back as duplicate_recent. If a request times out, wait 20 minutes, then send it again. Recipients it already mailed come back as duplicates; the rest are mailed once.A body over 4.25 MB can get the hosting platform's own 413, without the { "code", "message" } body.The examples show every error this API returns. See the Error reference for what to do about each.
{
"code": "invalid_api_key",
"message": "We could not verify that API key. Check the key and try again."
}{
"code": "campaign_not_found",
"message": "We could not find a campaign with that campaign ID."
}{
"code": "campaign_not_active",
"message": "That campaign is not active, so we did not mail anyone."
}{
"code": "design_cannot_render",
"message": "This campaign can't be placed yet. \"Gradient banner\" on the front of \"Spring open house\" uses a setting we can't print. Change or delete it in the designer, then try again."
}{
"code": "design_cannot_render",
"message": "Open and save this AI draft in the designer before using it in a campaign."
}{
"code": "payload_too_large",
"message": "That request was too large. Send up to 5,000 recipients per request."
}{
"code": "invalid_payload",
"message": "We could not read that request. Check the format and try again."
}HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"code": "rate_limited",
"message": "Too many requests. Wait a moment and try again."
}{
"code": "internal_error",
"message": "Something went wrong on our end. Nothing was mailed. Try again shortly."
}{
"code": "validation_unavailable",
"message": "We could not check addresses just now, so nothing was mailed. Try again in a few minutes. If it keeps happening, contact support."
}Integrations push API
A failed request returns { "error": { "code", "message", "details" } } with its HTTP status. details appears only on some codes, with machine-readable detail like the limit you went over. A batch that was accepted returns 200 and lists one rejection per refused record and the dropped values grouped by field instead.
A body over the hosting platform's cap can get the platform's own 413, without this body.
The examples show every error the push API returns. See the Error reference for what to do about each.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="Postaged"
{
"error": {
"code": "invalid_key",
"message": "That API key is not valid for this integration."
}
}{
"error": {
"code": "integration_unavailable",
"message": "This integration is not accepting data. Contact support if you think that is wrong."
}
}HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Slow down and retry."
}
}{
"error": {
"code": "invalid_json",
"message": "The request body must be JSON."
}
}{
"error": {
"code": "invalid_request",
"message": "Send { fields: [...] } describing your columns."
}
}{
"error": {
"code": "too_many_fields",
"message": "You declared 1001 fields. The limit is 1000.",
"details": { "limit": 1000, "received": 1001 }
}
}{
"error": {
"code": "field_locked",
"message": "These fields have a type set in Postaged and cannot be changed from here.",
"details": {
"fields": ["deal_value"]
}
}
}{
"error": {
"code": "missing_idempotency_key",
"message": "Send an Idempotency-Key header so a retry cannot open a second sync."
}
}{
"error": {
"code": "sync_in_progress",
"message": "A sync is already open for this integration. Complete it before beginning another.",
"details": {
"syncId": "0b6f3c3e-8d2a-4f7e-9a51-2c4d8e6f1a20",
"mode": "full",
"expiresAt": "2026-10-04T14:05:00.000Z"
}
}
}{
"error": {
"code": "idempotency_key_reused",
"message": "That Idempotency-Key was used for a different request. Use a new key."
}
}{
"error": {
"code": "in_progress",
"message": "That request is still being processed."
}
}{
"error": {
"code": "no_open_sync",
"message": "There is no open sync to complete."
}
}{
"error": {
"code": "too_many_records",
"message": "You sent 1001 records. Send at most 1000 per batch.",
"details": { "limit": 1000, "received": 1001 }
}
}{
"error": {
"code": "payload_too_large",
"message": "A batch may be at most 4456448 bytes.",
"details": { "limit": 4456448 }
}
}{
"error": {
"code": "invalid_sync_id",
"message": "syncId must be the id a sync call returned."
}
}{
"error": {
"code": "invalid_type",
"message": "type must be \"rejection\" or \"warning\"."
}
}{
"error": {
"code": "invalid_cursor",
"message": "after must be the nextAfter value of the previous page."
}
}{
"error": {
"code": "invalid_limit",
"message": "limit must be a whole number from 1 to 1000."
}
}{
"error": {
"code": "sync_not_found",
"message": "There is no sync with that id on this integration."
}
}Integrations MCP server
- JSON-RPC errors come back with HTTP
200, as{ "jsonrpc": "2.0", "id", "error": { "code", "message" } }. - Authentication, availability, and rate-limit failures use the push API's envelope with HTTP
401,403, or429. AGETto the endpoint gets405 use_post. - Tool-level failures come back as a tool result whose text carries
errorandmessage, so your agent can read them and recover.
See the Error reference for every code.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="Postaged"
{
"error": {
"code": "invalid_key",
"message": "That API key is not valid for this integration."
}
}HTTP/1.1 403 Forbidden
{
"error": {
"code": "integration_unavailable",
"message": "This integration is not accepting data. Contact support if you think that is wrong."
}
}HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Slow down and retry."
}
}HTTP/1.1 405 Method Not Allowed
{
"error": {
"code": "use_post",
"message": "This MCP endpoint speaks JSON-RPC over POST."
}
}{
"jsonrpc": "2.0",
"id": null,
"error": { "code": -32700, "message": "The request body must be JSON-RPC." }
}{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32600, "message": "A JSON-RPC method is required." }
}{
"jsonrpc": "2.0",
"id": null,
"error": { "code": -32600, "message": "That request is too large." }
}{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32601, "message": "Unknown tool: send_mail" }
}{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32601, "message": "Unknown method: resources/subscribe" }
}{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32603, "message": "That call could not be completed." }
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"sync_in_progress\",\n \"message\": \"A sync is already open. Complete it before beginning another.\"\n}"
}
]
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"no_open_sync\",\n \"message\": \"Call begin_sync first.\"\n}"
}
]
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"too_many_records\",\n \"message\": \"Send at most 1000 records per batch.\"\n}"
}
]
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"no_open_sync\",\n \"message\": \"There is no sync to complete.\"\n}"
}
]
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"sync_not_found\",\n \"message\": \"There is no sync with that id on this integration.\"\n}"
}
]
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"invalid_sync_id\",\n \"message\": \"syncId must be the id a sync call returned.\"\n}"
}
]
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"invalid_type\",\n \"message\": \"type must be \\\"rejection\\\" or \\\"warning\\\".\"\n}"
}
]
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"invalid_cursor\",\n \"message\": \"after must be the nextAfter value of the previous page.\"\n}"
}
]
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"error\": \"invalid_limit\",\n \"message\": \"limit must be a whole number from 1 to 1000.\"\n}"
}
]
}
}