Errors
Every Kirisan API response is JSON. Successful calls include "status": true. Failures include "status": false and a human-readable "reason" when the whole request failed before channel delivery.
HTTP status codes
| Status | When it happens |
|---|---|
200 | Request parsed — check "status" in the body (send may return 200 with "status": false and channel details) |
400 | Invalid JSON, missing fields, or validation error |
401 | Missing or invalid Bearer token |
403 | Bearer token valid but a channel key does not belong to your account |
503 | Service temporarily unavailable |
POST /v1/send usually returns HTTP 200 even when delivery fails — read "status" and "channels".
Top-level reason
These appear on the response root when the request never reached delivery, or when every channel failed in a blocking way:
| Reason (examples) | Meaning |
|---|---|
missing api token / invalid api token | Fix Authorization: Bearer … — see Authentication |
invalid JSON | Body is not valid JSON |
No channels configured | keys is empty — include at least one channel token |
content required | No content object with at least one channel |
no sendable channels: content requires a matching key | You set content.whatsapp but omitted keys.whatsapp, or similar |
Invalid schedule: the time is in the past. | context.timing.schedule must be a future Unix timestamp |
whatsapp key does not belong to this account | Channel token belongs to another user (403) |
Validation errors from message bodies are also returned as top-level "reason" before send — for example email inline message requires subject or waba message type must be text, interactive, image, video, or document.
Per-channel errors (channels)
On POST /v1/send, each channel you attempted has its own object under "channels":
{
"status": false,
"channels": {
"whatsapp": {
"status": false,
"reason": "template is not approved for sending",
"processing_time": "142ms"
},
"email": {
"status": true,
"processing_time": "891ms"
}
}
} | Field | Meaning |
|---|---|
status | true if that channel delivered (or was scheduled) |
reason | Present when status is false — quota, missing target, template inactive, provider error, etc. For email, SMTP and transport errors are summarized into short readable sentences (for example Recipient address does not exist). Raw provider text is kept in internal logs only. Unrecognized email failures return Email could not be delivered. Quota messages such as insufficient quota and paid plan expired are returned unchanged. |
detail | Extra provider payload on some channels (WhatsApp, Telegram, WABA) |
processing_time | How long the provider call took |
scheduled | true when the message was queued for context.timing.schedule |
fallback | true when this channel sent as part of a fallback tier |
When one channel fails and another succeeds (for example WhatsApp fails, email fallback succeeds), top-level "status" may still be true if any channel delivered.
Template errors on send
When content.<channel>.template points to a dashboard template that cannot send:
| Reason | Meaning |
|---|---|
template is not approved for sending | Template is still pending review or was rejected |
template is inactive | Template exists but is turned off in the dashboard |
WhatsApp template not found | Wrong ID, wrong channel, or template belongs to another account |
Create templates under Channel → channel → Templates and confirm they are Approved and Active before referencing their ID on send.
Email errors
These reasons appear on "channels.email.reason" (or on the root if email was the only channel that failed):
| Reason | Meaning | What to do |
|---|---|---|
rate_limited | You sent more than 2 requests per second of marketing or no-template email on the same domain. The response may include retry_after (seconds). | Wait, then send again. Transactional templates do not use this cap. |
queue_full | Too many marketing or no-template emails are still waiting for this domain. | Wait until some send, then try again. |
email_lane_blocked | That kind of email (transactional, marketing, or raw) is blocked on the domain. | Open Channel → Email → Domains, read the label under Sending health, then Acknowledge. |
email_domain_suspended | Domain health paused that kind of email. | Open Channel → Email → Domains. The badge may show Suspended until …. Wait or acknowledge as shown. |
not_in_email_pool | A marketing template to an address that has not received email from this domain yet. | Send a transactional or no-template email to that address first, or use an address you have already emailed. |
email inline message requires subject | A no-template message is missing subject. | Add subject. |
email inline message requires message or html | A no-template message has no body. | Add message and/or html. |
email inline message type must be text | message.type is not text. | Set type to "text". |
SMTP delivery failures
When the email service rejects delivery to Gmail, Outlook, or Yahoo, channels.email.reason uses a stable code (not raw SMTP text). Full SMTP diagnostics remain in Kirisan logs (admin).
| Reason | Meaning | What to do |
|---|---|---|
recipient_not_found | Recipient address does not exist or is invalid (hard bounce). | Remove or fix the address; it is permanently suppressed. |
recipient_temp_unavailable | Mailbox full or recipient temporarily unavailable (soft bounce). | Retry later; address is suppressed for 24h (3× → permanent). |
delivery_temporarily_unavailable | Temporary deferral (greylist, busy server, timeout). | Response may include retryable: true. Retry later. |
rate_limited_by_provider | Provider throttled the sender (IP/domain/reputation), not the recipient address. | Slow down volume; fix auth/warmup. Do not remove the address from your list. |
blocked_by_provider | IP/domain on a provider block list (e.g. Outlook S3150). | Fix reputation; request delisting if needed. Recipient address stays valid. |
policy_rejected | Auth/policy failure (SPF, DMARC, content, Outlook SC-/DY-/OU-). | Fix DNS auth or message content. |
message_rejected | Message size, format, or syntax rejected. | Reduce attachments or fix format. |
rate_limited (2 requests/second per domain) is separate from rate_limited_by_provider (Gmail/Outlook/Yahoo sender limits).
See the Send API for the three kinds of email, and Sending messages if a send is rejected or stuck.
Related
- Authentication — tokens and channel keys
- Send API — request shape, three kinds of email, and fallback behavior
- Send messages — same fallback tiers in the dashboard
- Sending messages — queued, rate limit, and domain Blocked