Get in Touch

Have a question about the platform, need help with your integration, or want to discuss partnership opportunities and enterprise pricing? Drop us an email — we’ll do our best to get back to you within 3–4 hours.

contact@fiwano.com

Working with an AI agent? Download this documentation as a Markdown file to use as context.

Download .md

Contents

Fiwano — API Documentation

Unified REST API for WhatsApp, Instagram and Facebook Messenger.

Base URL /api/v1
Format JSON
Auth X-API-Key header
n8n nodes n8n-nodes-fiwano on npm

Channel Capabilities

All three channels are connected the same way (OAuth). The table below shows what each channel supports.

Feature WhatsApp Instagram Facebook Messenger
Outbound messages Text (for media, use templates with media headers) Text only Text only
Template messages ✅ Required outside 24h window ❌ Not supported ❌ Not supported
Incoming webhooks text; all others as unsupported with unsupported_type text; media as unsupported text; attachments as unsupported
Delivery statuses sent delivered read failed delivered read delivered read
Recipient format Phone number without + (e.g. 1234567890) IGSID — from data.from in webhooks PSID — from data.from in webhooks
24h window workaround Use approved templates None — wait for user to message None — wait for user to message
Channel identifier phone_number_id ig_account_id page_id
Sender profile data.from_name (from Meta contacts) Via profile endpoint Via profile endpoint

Note: Each Meta account (phone number, Instagram account, or Facebook Page) can only be connected to one Fiwano user at a time.


Authentication

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 -H "X-API-Key: mip_live_abc123..." \
     https://fiwano.com/api/v1/channels

All keys start with mip_live_. Keys are hashed on our side.


Connecting Channels

Option A: Via Portal (Self-service)

  1. Go to Channels → Connect Channel in the portal.
  2. Select the channel type (WhatsApp, Instagram, or Facebook Messenger).
  3. Complete the Meta OAuth flow in the popup window.
  4. Configure the Webhook URL and select Webhook Events in channel settings.
  5. By default, no events are enabled — select which events to forward to your endpoint.

A webhook_secret is auto-generated when you first set a webhook URL. Use it to verify incoming webhook signatures.

Option B: Via API (Programmatic)

Use this when your application connects channels on behalf of your end users.

Step 1. Register redirect URIs — whitelist the URL(s) where users will be redirected after OAuth:

POST /api/v1/redirects
{ "uri_pattern": "https://yourapp.com/callback" }

Step 2. Request a setup URL (valid for 10 minutes):

POST /api/v1/channels/setup-url
{
  "channel_type": "whatsapp",
  "redirect_uri": "https://yourapp.com/callback"
}

Response contains setup_url — open it in a browser or popup for the user.

Step 3. User completes Meta OAuth — after approval, they are redirected to your redirect_uri with a one-time code parameter:

https://yourapp.com/callback?code=abc123...

If the user cancels: ?error=access_denied

Step 4. Exchange the code + configure webhook (within 5 minutes, single-use):

POST /api/v1/channels/exchange-code
{
  "code": "abc123...",
  "webhook_url": "https://yourapp.com/webhooks/meta",
  "webhook_secret": "your-optional-secret",
  "webhook_events": ["message.received", "message.delivered", "message.failed"]
}

Returns channel_id, channel details, and webhook_secret (auto-generated if not provided). All fields except code are optional — you can set them later via PATCH /api/v1/channels/{id}.

Important: By default, no events are delivered. You must explicitly set webhook_events to receive webhooks.


Channels

GET /api/v1/channels

List all channels for your account (both active and inactive).

Response:

{
  "channels": [
    {
      "id": "a1b2c3d4e5f67890",
      "channel_type": "whatsapp",
      "name": "My Business",
      "is_active": true,
      "phone_number_id": "123456789",
      "phone_number": "+1234567890",
      "waba_id": "987654321",
      "webhook_url": "https://yourapp.com/webhooks",
      "has_webhook_secret": true,
      "connected_at": "2025-01-15T10:30:00",
      "token_expires_at": "2025-03-15T10:30:00"
    }
  ],
  "total": 1
}

Response fields:

Field Description
id Channel ID (use in all other API calls)
channel_type whatsapp, instagram, or facebook
name Display name (business name, username, or page name)
is_active true if the channel can send/receive messages
phone_number_id WhatsApp only — Meta's phone number ID
phone_number WhatsApp only — human-readable phone number
waba_id WhatsApp only — WhatsApp Business Account ID
ig_account_id Instagram only — Instagram account ID
ig_username Instagram only — Instagram username
page_id Instagram/Facebook — linked Facebook Page ID
webhook_url Where incoming messages are delivered
has_webhook_secret Whether a webhook secret is configured
webhook_events List of enabled event types (e.g. ["message.received", "message.delivered"])
token_expires_at When the access token expires (auto-refreshed 7 days before)

GET /api/v1/channels/{channel_id}

Get details of a specific channel. Same response format as the list endpoint.

POST /api/v1/channels/setup-url

Generate an OAuth URL to connect a new channel. See API connection flow above.

Field Type Required Description
channel_type string Yes whatsapp, instagram, or facebook
redirect_uri string Yes Must be in your allowed redirect URIs

Response:

{
  "setup_url": "https://fiwano.com/setup/start?session=...",
  "session_id": "e7f8a1b2c3d45678",
  "expires_at": "2025-01-15T10:40:00"
}

The setup URL is valid for 10 minutes.

POST /api/v1/channels/exchange-code

Exchange a one-time completion code for channel data. Optionally configure webhook in the same call.

Field Type Required Description
code string Yes One-time code from the OAuth redirect. Expires in 5 minutes, single-use.
webhook_url string No HTTPS URL for incoming webhooks. If provided, webhook is configured automatically.
webhook_secret string No Custom HMAC secret. If webhook_url is set and this is omitted, a secret is auto-generated.
webhook_events string[] No List of event types to deliver. See Event Types for available values per channel type. If omitted, no events are delivered until configured.

WhatsApp response:

{
  "channel_id": "a1b2c3d4e5f67890",
  "channel_type": "whatsapp",
  "name": "My Business",
  "phone_number_id": "123456789",
  "phone_number": "+1234567890",
  "webhook_url": "https://yourapp.com/webhooks",
  "webhook_secret": "a1b2c3d4e5f6...",
  "webhook_events": ["message.received", "message.delivered", "message.failed"]
}

Instagram response:

{
  "channel_id": "b2c3d4e5f6789012",
  "channel_type": "instagram",
  "name": "mybusiness",
  "ig_account_id": "17841400123456",
  "ig_username": "mybusiness",
  "webhook_url": "https://yourapp.com/webhooks",
  "webhook_secret": "a1b2c3d4e5f6...",
  "webhook_events": ["message.received", "message.delivered"]
}

PATCH /api/v1/channels/{channel_id}

Update channel webhook settings.

Field Type Required Description
webhook_url string No HTTPS URL for incoming webhooks
webhook_secret string No Custom HMAC secret. If omitted and no secret exists, one is auto-generated.
webhook_events string[] No List of event types to deliver. Only events valid for the channel type are accepted. Empty array disables all events.

Example — enable only incoming messages and failures:

{
  "webhook_events": ["message.received", "message.failed"]
}

Response includes the current webhook_events list.

DELETE /api/v1/channels/{channel_id}

Deactivate a channel (soft delete). The channel stops sending and receiving messages. You can reconnect the same Meta account later via a new OAuth flow.


Sending Messages

POST /api/v1/messages/send

Send a text message through a connected channel. Works for all channel types.

Field Type Required Description
channel_id string Yes Channel to send from
recipient string Yes See recipient format in channel capabilities table
text string Yes Message text

WhatsApp example:

{
  "channel_id": "a1b2c3d4e5f67890",
  "recipient": "1234567890",
  "text": "Hello! Your order is ready."
}

Instagram example:

{
  "channel_id": "b2c3d4e5f6789012",
  "recipient": "6543217890123456",
  "text": "Thanks for reaching out!"
}

Response:

{
  "success": true,
  "message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

The message_id (UUID) is used in all subsequent delivery status webhooks.

POST /api/v1/messages/send-template

WhatsApp only. Send a template message to initiate conversations outside the 24-hour window. Only templates with status APPROVED can be sent.

Field Type Required Description
channel_id string Yes WhatsApp channel ID
template_name string Yes Template name (e.g. order_confirmation)
language string Yes Language code (e.g. en_US)
recipient string Yes Phone number without +
variables object No Variable values keyed by component type. Omit if no variables.

Positional variables example:

{
  "channel_id": "a1b2c3d4e5f67890",
  "template_name": "order_confirmation",
  "language": "en_US",
  "recipient": "1234567890",
  "variables": {
    "header": ["Summer Sale"],
    "body": ["Pablo", "ORD-123", "25%"],
    "buttons": [{"index": 0, "value": "promo25"}]
  }
}

Named variables example:

{
  "channel_id": "a1b2c3d4e5f67890",
  "template_name": "welcome_message",
  "language": "en_US",
  "recipient": "1234567890",
  "variables": {
    "body": {
      "customer_name": "Pablo",
      "order_number": "ORD-123"
    }
  }
}

Template without variables — omit variables entirely.


WhatsApp Templates

WhatsApp requires pre-approved message templates to start conversations outside the 24-hour customer service window. Templates are submitted to Meta for review (typically < 24 hours). This section only applies to WhatsApp channels.

Template lifecycle: Create → PENDING (under Meta review) → APPROVED (can be sent) or REJECTED (fix and resubmit).

GET /api/v1/channels/{channel_id}/templates

List templates for a WhatsApp channel. By default, syncs with Meta before returning.

Query param Default Description
sync true Sync from Meta before returning. Set false for cached data (faster).
status all Filter: APPROVED, PENDING, REJECTED

GET /api/v1/channels/{channel_id}/templates/{template_id}

Get template details including components and variable definitions.

POST /api/v1/channels/{channel_id}/templates

Create a new template (submitted to Meta for review, starts as PENDING).

Field Type Required Description
name string Yes Lowercase alphanumeric + underscores, max 512 chars
category string Yes MARKETING, UTILITY, or AUTHENTICATION
language string Yes Language code (e.g. en_US, ru, es)
components array Yes Template components. BODY required. HEADER (text only), FOOTER, BUTTONS optional.
parameter_format string No positional (default: {{1}}, {{2}}) or named ({{customer_name}})

Example:

{
  "name": "order_confirmation",
  "category": "UTILITY",
  "language": "en_US",
  "components": [
    {
      "type": "BODY",
      "text": "Hi {{1}}, your order {{2}} is confirmed.",
      "example": {"body_text": [["Pablo", "ORD-123"]]}
    }
  ],
  "parameter_format": "positional"
}

The example field is required by Meta for review.

PUT /api/v1/channels/{channel_id}/templates/{template_id}

Update template components. All components are replaced entirely (no partial update).

Field Type Required Description
components array Yes New components (replaces all existing)
category string No New category (only for REJECTED or PAUSED templates)

Restrictions: Approved templates can be edited max 10 times per 30 days, and only once per 24 hours. After editing, the template goes back to PENDING for re-review.

DELETE /api/v1/channels/{channel_id}/templates/{template_id}

Delete a template.

Query param Default Description
all_languages false Delete all language versions of this template

Warning: After deleting an APPROVED template, you cannot create a new template with the same name for 30 days (Meta restriction).


Incoming Webhooks

When a message arrives on your connected channel, we deliver it to your channel's webhook_url as a POST request with a JSON body. Each delivery is signed with your channel's webhook_secret.

Verifying Signatures

Every webhook request includes an X-Webhook-Signature header:

X-Webhook-Signature: sha256=<hmac_hex>

To verify: compute HMAC-SHA256 of the raw request body using your webhook_secret as the key, then compare the hex digest.

import hmac, hashlib

def verify_signature(body: bytes, secret: str, header: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    received = header.replace("sha256=", "")
    return hmac.compare_digest(expected, received)

Event Types

Each channel type supports a specific set of webhook events. Only events you explicitly enable via webhook_events are delivered. By default, no events are enabled — you must configure them after connecting a channel.

Event Description Channels
message.received Incoming message from a user All
message.sent Your message was accepted by Meta WhatsApp
message.delivered Message delivered to recipient's device WhatsApp, Instagram, Facebook
message.read Message read by recipient * WhatsApp, Instagram, Facebook
message.failed Message delivery failed WhatsApp

* message.read for WhatsApp depends on recipient's privacy settings — if read receipts are disabled, the read status will never arrive. Treat delivered as a terminal success state.

Available events per channel type:

Channel Available events
WhatsApp message.received, message.sent, message.delivered, message.read, message.failed
Instagram message.received, message.delivered, message.read
Facebook message.received, message.delivered, message.read

Delivery Status Tracking

When you send a message via /api/v1/messages/send, you receive a message_id (UUID). All subsequent status webhooks reference this same UUID.

Payload Format

All payloads share the same top-level structure:

{
  "event": "message.received",
  "channel_id": "a1b2c3d4e5f67890",
  "channel_type": "whatsapp",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": { ... }
}

message.received (WhatsApp)

{
  "event": "message.received",
  "channel_id": "a1b2c3d4e5f67890",
  "channel_type": "whatsapp",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "message_id": "wamid.xxx",
    "from": "1234567890",
    "from_name": "John Doe",
    "type": "text",
    "text": "Hello!"
  }
}

data.from — sender's phone number without +. Use directly as recipient when replying.

message.received (Instagram)

{
  "event": "message.received",
  "channel_id": "b2c3d4e5f6789012",
  "channel_type": "instagram",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "message_id": "mid.xxx",
    "from": "6543217890123456",
    "from_name": null,
    "type": "text",
    "text": "Hi there!"
  }
}

data.from — IGSID. Use as recipient when replying. from_name is always null (Meta does not include sender name in IG webhooks).

message.received (Facebook Messenger)

{
  "event": "message.received",
  "channel_id": "c3f8a1b2e4d56789",
  "channel_type": "facebook",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "message_id": "mid.xxx",
    "from": "7890123456789012",
    "from_name": null,
    "type": "text",
    "text": "Hello from Messenger!"
  }
}

data.from — PSID. Use as recipient when replying. from_name is always null (Meta does not include sender name in FB webhooks).

message.received — unsupported type (all channels)

{
  "event": "message.received",
  "channel_id": "a1b2c3d4e5f67890",
  "channel_type": "whatsapp",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "message_id": "wamid.xxx",
    "from": "1234567890",
    "from_name": "John Doe",
    "type": "unsupported",
    "unsupported_type": "image"
  }
}

Non-text messages (image, video, audio, document, location, sticker, etc.) are delivered with type: "unsupported" and the original type in unsupported_type.

message.delivered / message.read (all channels)

{
  "event": "message.read",
  "channel_id": "b2c3d4e5f6789012",
  "channel_type": "instagram",
  "timestamp": "2025-01-15T10:30:10Z",
  "data": {
    "message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "read",
    "recipient": "6543217890123456"
  }
}

Same format for all channels and all statuses (sent, delivered, read). message_id is the UUID from the send response.

message.failed (WhatsApp)

{
  "event": "message.failed",
  "channel_id": "a1b2c3d4e5f67890",
  "channel_type": "whatsapp",
  "timestamp": "2025-01-15T10:30:05Z",
  "data": {
    "message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "recipient": "1234567890",
    "status": "failed",
    "error": "Message undeliverable",
    "errors": [{"code": 131047, "title": "Message undeliverable"}]
  }
}

Retry Policy

If your webhook URL returns a non-2xx status or is unreachable, the system retries automatically:

Important: Your endpoint must respond with HTTP 2xx within 5 seconds. Non-2xx responses or timeouts trigger the retry queue. Incoming messages from Meta are always saved on our side, even if relay fails.

Tip: Only enable the webhook events you actually handle. Unhandled events that receive non-2xx responses will fill your retry queue unnecessarily.

Sender Profile

Sender name is not included in webhook payloads for Instagram and Facebook (data.from_name is null). To get sender profile data, use the dedicated profile endpointGET /api/v1/channels/{channel_id}/profile/{user_id}.

For WhatsApp, data.from_name is always present in webhooks (Meta includes it in the payload). No separate API call needed.


Sender Profile

Fetch sender profile data from Meta Graph API. Use this to get the name, profile picture, and other details of users who message your channel.

Results are cached for 5 minutes on our side. The cached field in the response indicates whether the result was served from cache.

Tip: Call this endpoint once when you first see a new data.from value, then cache the result on your side. No need to call it on every message.

GET /api/v1/channels/{channel_id}/profile/{user_id}

Parameter In Description
channel_id path Channel ID
user_id path Sender identifier — IGSID (Instagram) or PSID (Facebook) from data.from in webhooks

Instagram example:

curl -H "X-API-Key: mip_live_abc123..." \
     https://fiwano.com/api/v1/channels/b2c3d4e5f6789012/profile/6543217890123456

Response (Instagram):

{
  "channel_id": "b2c3d4e5f6789012",
  "channel_type": "instagram",
  "user_id": "6543217890123456",
  "profile": {
    "username": "johndoe",
    "name": "John Doe",
    "profile_pic": "https://scontent.cdninstagram.com/...",
    "follower_count": 1500,
    "is_verified_user": false
  },
  "cached": false
}

Response (Facebook):

{
  "channel_id": "c3f8a1b2e4d56789",
  "channel_type": "facebook",
  "user_id": "7890123456789012",
  "profile": {
    "first_name": "John",
    "last_name": "Doe",
    "profile_pic": "https://platform-lookaside.fbsbx.com/..."
  },
  "cached": true
}

If the profile is unavailable (private account, invalid ID):

{
  "channel_id": "b2c3d4e5f6789012",
  "channel_type": "instagram",
  "user_id": "6543217890123456",
  "profile": null,
  "cached": false
}
HTTP Status Meaning
200 Profile returned (may be null if unavailable)
400 WhatsApp channel (not supported), inactive channel, or expired token
404 Channel not found
502 Meta API error (retry may help)

Redirect URIs

Manage allowed redirect URIs for your API key. Required for the programmatic channel connection flow.

GET /api/v1/redirects

List allowed redirect URIs for the current API key.

POST /api/v1/redirects

Add an allowed redirect URI.

Field Type Required Description
uri_pattern string Yes HTTPS URL or wildcard pattern (e.g. https://*.example.com/callback)

DELETE /api/v1/redirects/{redirect_id}

Remove an allowed redirect URI.


Errors & Rate Limits

HTTP Status Codes

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 — go to Billing
404 Not found Resource doesn't exist or belongs to another account
429 Rate limit exceeded Wait and retry. Limit: 100 req/min per API key.
502 Meta API error Upstream failure. Check detail. Retry may help.

Error Format

{ "detail": "Human-readable error description" }

Rate Limits

100 requests per minute per API key. HTTP 429 when exceeded. Meta has its own per-channel limits (shown in Meta Business Manager, not controlled by Fiwano).


n8n Integration

Official Fiwano community nodes for n8n are available on npm: n8n-nodes-fiwano.

Use them to build WhatsApp, Instagram and Facebook Messenger automations, AI agent workflows, and chatbots without writing custom API integration code.

Install

npm install n8n-nodes-fiwano

Or from n8n UI: Settings → Community Nodes → Install → enter n8n-nodes-fiwano.

Nodes

Node Type Description
Fiwano Action Send messages, manage channels, WhatsApp templates, contact profile enrichment, redirect URIs
Fiwano Trigger Webhook Trigger Receive incoming messages and delivery status webhooks with built-in HMAC signature verification

Action node — operations

Resource Operations
Message Send Text, Send Template (WhatsApp)
Channel Get Many, Get, Generate OAuth URL, Exchange OAuth Code, Update Webhook, Delete
Contact Get Profile (Instagram, Facebook — returns name, profile picture, follower count)
Template Get Many, Get, Create, Delete (WhatsApp only)
Redirect URI Get Many, Add, Delete

Trigger node — events

Starts your workflow for any of these events (filter by event type in node settings):

Event Channels
message.received WhatsApp, Instagram, Facebook
message.delivered WhatsApp, Instagram, Facebook
message.read WhatsApp, Instagram, Facebook
message.sent WhatsApp
message.failed WhatsApp

HMAC-SHA256 signature verification is built in — the trigger validates every webhook automatically.

Example workflow

A ready-to-import demo workflow is included in the GitHub repository covering every operation: echo bot with profile enrichment, channel management, template CRUD, text and template messaging.


Important Notes

Token lifecycle — Meta tokens expire ~60 days after connection. Fiwano automatically refreshes them 7 days before expiry. If auto-refresh fails (e.g. user revoked permissions in Meta), the channel goes inactive and needs reconnection via OAuth.

Channel uniqueness — Each Meta account can only be connected to one Fiwano user at a time. HTTP 400 with "Already connected to another account" if it's already connected by someone else.

Billing — New accounts get a 7-day free trial. After expiry, sending and webhook relay are blocked until you subscribe. Incoming messages are still saved and will be forwarded once the subscription is active.

Inactive channels — A channel goes inactive if deleted via API, if the token can't be refreshed, or if the user revokes Meta permissions. Reconnect via a new OAuth flow.

WhatsApp 24-hour window — You can send regular text messages only within 24 hours of the customer's last message. Outside this window, use approved templates via /api/v1/messages/send-template. This is a Meta policy.

Instagram & Facebook 24-hour window — You can reply only within 24 hours of the user's last message. No template workaround — wait for the user to message again.

Text-only messaging — Fiwano supports sending text messages only. For media via WhatsApp, use templates with media headers. Incoming non-text messages are delivered with type: "unsupported".

Fiwano API Documentation