Skip to main content

Error format

All error responses return a JSON body with an error field. Some include a machine-readable code for programmatic handling.

HTTP status codes


Send Message errors (POST /api/messages)

These are the specific errors you may encounter when sending a message.
Pre-send checks like line limits, time windows, device gaps, and contact gaps never return errors. The message is accepted with 201 and the status field indicates the outcome (pending, scheduled, etc.).
Neither message nor attachmentUrls was provided (or both were empty).
Fix: Include at least one of: a non-empty message string, or an attachmentUrls array with up to 2 URLs.
The messageType value is not one of the accepted values.
Fix: Use "imessage", "sms", or "email". Or omit it entirely to default to "imessage".
No fromLineId was provided and the workspace has no active lines to round-robin from.
Fix: Provision at least one line in your workspace, or pass fromLineId explicitly.
No recipientPhone, recipientEmail, or leadId was provided, and the resolved lead (if any) has no contact details.
Fix: Provide at least one of recipientPhone, recipientEmail, or a leadId that has stored contact details.
sendFallbackSmsOnFailed was set to true but the workspace doesn’t have Twilio fallback configured.
Fix: Configure Twilio on your workspace via PATCH /api/workspace/fallback-config, or set sendFallbackSmsOnFailed to false.
The API key is missing, invalid, or expired.
Fix: Check your Authorization: Bearer <key> header. Generate a new key from your workspace settings if needed.
The workspace subscription is past due and write operations are blocked.
Fix: Update your billing in the Tuco dashboard.
A leadId was provided but no matching lead exists in the workspace.
Fix: Verify the leadId exists and belongs to the same workspace as the API key.

Status vs error — key distinction

A 201 response with status: "pending" or status: "scheduled" is not an error. It means the message was accepted and Tuco will send it when conditions are met (time window, line limits, device gap, etc.).Only 4xx and 5xx responses indicate actual errors.

Rate limits

Tuco applies per-workspace rate limits to protect the platform. When you hit a limit:
  • You receive a 429 response.
  • The Retry-After header tells you how many seconds to wait before retrying.
  • No data is modified — safe to retry.
All limits are per workspace (organization), regardless of how many API keys or users are calling the endpoint.
POST /api/drip is exempt from the 60/min send limit and never returns 429 for a normal blast — that is the whole point of the endpoint (a 429 would trigger your CRM’s retry storm and duplicate sends). It applies only a very high infra backstop on the buffered intake (~1,000 req/sec sustained / 10,000 burst per workspace) that trips solely on runaway/abuse; if you ever hit it you get 429 + Retry-After and can safely retry. Real send pacing (150/day, ~3–7 new conversations/hr per line) is handled automatically by the per-line ban protector at send time, not at intake. See Drip.
Availability checks have two limits:
  1. 200 req/min API rate limit (token bucket, per workspace)
  2. 70 checks/day per line hard cap (each check hits a real device). With 3 lines = 210/day total.
When the daily cap is exhausted (or no line is healthy), the check can’t be completed, so it returns 200 with { "available": false, "verified": false, "reason": "daily_quota_exhausted" } (or "reason": "no_healthy_line") — not a 429. This keeps CRM workflow steps from failing on an un-checkable contact. Use verified: false to tell “couldn’t check” apart from a confirmed not-on-iMessage result (available: false with no verified flag). Only the per-minute rate limit (#1 above) returns 429 with Retry-After.Availability checks do not consume your daily send quota — they use separate counters.
For API integrators: Skip availability checking entirely. Just call POST /api/messages and listen for the message.fallback webhook to trigger your SMS/WhatsApp fallback. This avoids burning your 70/day availability quota. See the check-availability-rr endpoint for the recommended pattern.
For high-volume sends, use campaigns instead of individual API calls. Campaigns handle pacing, line rotation, and retry logic automatically — and are not subject to per-request rate limits.