Built for people and AI agents
Every page here is human-readable — task guides and concepts — backed by a complete OpenAPI contract. The same reference is bundled in an open cookbook you can clone into your project in seconds.
Working with an AI agent? Clone the cookbook
RecommendedPoint your agent at PLAYBOOK.md — a step-by-step guide to the API reference and working examples, so it reads only what each step needs.
View the cookbook on GitHubPrefer to paste context instead? Download the full docs as one Markdown file, or grab just the raw spec.
All API requests require an API key in the X-API-Key header.
Create a key from the API Keys page in the portal. The full key is shown only once — save it securely. Lost keys cannot be recovered; revoke and create a new one.
curl https://fiwano.com/api/v1/channels \
-H "X-API-Key: YOUR_API_KEY"
All keys start with mip_live_. Keys are hashed on our side.
Every error response has a detail field. Most domain errors use a human-readable string:
{ "detail": "Human-readable error description" }
Schema validation (422) uses a list of field errors. Some domain validation errors
instead use a structured detail object with a code field — for example
text_too_long when sending overlong text, invalid_recipient (400) when recipient
is empty, contains no digits, or is not a numeric PSID/IGSID on a Messenger/Instagram
channel (reason says which; hint says what to send instead), or
recipient_equals_sender (400) when a WhatsApp send is addressed to the channel's own
number. The per-endpoint shapes are in the API Reference.
| Code | Meaning | What to do |
|---|---|---|
200 |
Success | — |
201 |
Created | — |
400 |
Bad request | Check the detail field |
401 |
Unauthorized | Check your X-API-Key header |
402 |
Payment required | Trial ended or subscription inactive — see Subscriptions & Billing |
404 |
Not found | Resource doesn't exist or belongs to another account |
422 |
Validation error | Check required fields, types, and field constraints in detail |
429 |
Rate limit exceeded | Back off and retry after Retry-After — see rate limits |
502 |
Meta API error | Upstream failure. Check detail. Retry may help. |
503 |
Temporarily overloaded | Transient load shedding. Retry after Retry-After. |
The three send endpoints answer 200 even when the send fails — the outcome is
in success, status, and error_code. See
Delivery and retries.
error_code is Meta's error code, passed through unchanged. It is present only
when the failure came from Meta; a rejection by Fiwano itself uses an HTTP
status code from the table above instead. error always carries a
human-readable description, and for media sends a hint about the likely cause.
error_code |
Meaning | Retried by Fiwano | What to do |
|---|---|---|---|
10, 200 |
Meta denies this action for the account | no | Not a token problem: the channel stays connected and keeps receiving. Read error (Meta's own text) and check the account in Meta Business Settings |
10 with another app is controlling this thread |
Instagram/Messenger: another connected app owns the conversation | no | Make Fiwano the default routing app or disconnect the other app — see Prerequisites |
100 |
Invalid parameter — Meta reuses this for several unrelated causes, including a file above the size cap | no | Read error for the specific cause; check media_url, media_type, recipient format, and file size |
190 |
Access token expired or revoked | no | Reconnect the channel |
368 |
Account temporarily blocked for policy violations | no | Resolve in Meta Business Manager |
551 |
Messenger/Instagram: this person cannot be messaged right now — they blocked the Page, closed the chat, restricted business messages, or never messaged the Page | no | Nothing on your side; only the person can lift it. Do not resend automatically |
803 |
Object does not exist or is unavailable | no | Check the recipient identifier |
131008 |
Required parameter missing | no | Fix the request payload |
131009 |
Parameter value invalid for this channel | no | Fix the request payload |
131026 |
Recipient is not reachable on this platform | no | Verify the recipient |
131047 |
24h re-engagement window closed | no | Send an approved WhatsApp template — see messaging windows |
131051 |
Unsupported message type for this channel | no | Check channel capabilities |
131052 |
Meta could not download media_url |
no | Verify the URL returns 200, Content-Type matches media_type, and the signature has not expired |
131053 |
Meta could not process the media | yes | Often transient; check format and size if it persists |
131056 |
Pair rate limit between this sender and recipient | yes | Slow down messages to that recipient |
131057 |
WhatsApp Business Account in maintenance mode (e.g. a throughput upgrade) | yes | Usually temporary; no action |
Codes outside this table are passed through as Meta returns them. Anything not recognised as permanent is treated as transient and retried.
Occasionally Meta takes longer than 30 seconds — sometimes more than a minute —
to answer a send, most often while it downloads a large media file. Fiwano does
not fail the message: the call returns success: true, status: "queued", and
the message_id is final. Fiwano then finishes the send with Meta on your
behalf. What you see:
message.sent / message.delivered / message.read webhooks for
that message_id, exactly as for a message that returned sent right away.failed and the
channel owner receives the delivery digest email.Do not resend on your side while the message is queued. The same applies
if your own HTTP client times out — see
Response time.
Fiwano has no version numbers. The API contract is v1 (https://fiwano.com/api/v1), and it has been stable since the public launch in March 2026. The service ships continuously; new capabilities are listed in the changelog (with an Atom feed).
Every change to v1 is additive:
What stays fixed: existing endpoints, field names, types and meanings; the X-API-Key authentication; the webhook signature scheme. New webhook event types are never enabled on your channels without your action — you opt in per channel via webhook_events.
What your integration needs to do to stay compatible: ignore fields it does not know and ignore event types it did not enable. Do not treat an unknown field or a new value in an open set as an error.
If a breaking change ever becomes unavoidable, it ships as a new API version alongside v1, announced in the changelog and by email in advance. v1 keeps working.