REST API v1 — Documentation
Fliok Developer API
Build custom integrations with Fliok using our REST API. Send messages and media, read conversations, contacts and templates, answer Instagram messages and comments, schedule Instagram posts, ask for payments with WhatsApp Pay, and receive signed real-time webhooks — all programmatically.
Base URLhttps://fliok.com/api/v1
API reference · Overview
Last updated
Quickstart
Five steps from a key to a signed webhook, with cURL you can paste. You can do all of them in a free developer sandbox before you pay for a plan.
Create a key
On the Pro or Enterprise plan, create a key on the API keys page and copy it: it is shown once. To try everything first without paying, make a developer sandbox (Integrations → API keys, then Create sandbox) and create a key inside it for every step below.
Check the key
GET /meanswers with your workspace and the key’s role and scopes. A401names why a key was refused.cURL curl https://fliok.com/api/v1/me \ -H "Authorization: Bearer wa_YOUR_API_KEY"Send a first message
A text needs the customer to have written to you in the last 24 hours; outside that window, send an approved template (see Sending). The
Idempotency-Keymakes a retry safe.cURL curl -X POST https://fliok.com/api/v1/send \ -H "Authorization: Bearer wa_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-12345-shipped" \ -d '{ "to": "+919876543210", "type": "text", "text": "Hello! Your order #12345 has been shipped." }'# Successful response from POST /send (200 OK) { "ok": true, "messageId": "4f6c1e2a-91d3-4a77-b0c2-5f1e8d9a3b44" } // The send is queued, not yet delivered. Poll GET /messages/{id} // or subscribe to the message.status webhook for the outcome.Register a webhook
Give a public https:// address and the events you want. The answer carries the signing secret (
whsec_…) once: store it.cURL curl -X POST https://fliok.com/api/v1/webhooks \ -H "Authorization: Bearer wa_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/webhooks/fliok", "events": ["message.received", "message.status"] }' # -> { "id": "<endpoint id>", "url": "…", "events": [ … ], "active": true, # "createdAt": "…", "secret": "whsec_…" }Receive one
Ask for a signed
webhook.testdelivery now, or wait for a customer to write. Check the signature on every delivery before trusting it: verifying signatures.cURL curl -X POST https://fliok.com/api/v1/webhooks/<endpoint id>/test \ -H "Authorization: Bearer wa_YOUR_API_KEY" # -> { "ok": true, "eventId": "…" } # Your server then receives a POST with the headers # x-webhook-id, x-webhook-event, x-webhook-timestamp, x-webhook-signature # and the signature is "sha256=" + HMAC-SHA256(secret, timestamp + "." + rawBody), hex.
Using an AI app instead of your own code? Claude, ChatGPT, Cursor and other AI apps can work in Fliok through its MCP server, under the same permissions as this API. See the MCP guide.
Base URL and requests
https://fliok.com/api/v1v1stableRequests and answers are JSON. A request body can be up to 64 KB for ordinary writes and 1 MB for bulk writes; POST /media takes a file of up to 16 MB. Reads return pages with a nextCursor.
Send a User-Agent header that names your app, such as my-app/1.0. fliok.com is behind Cloudflare, which refuses some default library agents before the request reaches the API — Python’s built-in urllib gets “error 1010”. cURL and Python’s requests work as they are; with any other client, set one.
Authentication
The REST API and webhooks are included on the Pro and Enterprise plans. A key on a workspace that is not on Pro or Enterprise answers 402 plan_feature_unavailable. Pro is charged when you subscribe, and you can cancel any time; the plan runs to the end of the period you paid for. To try the API before you pay, use a developer sandbox.
Fliok API uses Bearer Token authentication. Include your API key in the Authorization header for every request. A key belongs to one workspace and carries one role for all of it — admin, agent or viewer, the same as a teammate — and can be revoked or given an expiry date from the dashboard. A rejected key returns 401 with a specific reason: api_key_expired, api_key_revoked or api_key_invalid, so you can tell a lapsed key from a missing one.
Allowed addresses. A key can be limited to IP addresses or CIDR ranges (IPv4 or IPv6). From anywhere else it answers 403 ip_not_allowed. The keys page shows who made each key and where it was last used from; removing a team member revokes the keys they made unless you choose to keep them. An address that keeps presenting keys that don't exist is refused with 429 for a while.
Money stays in the dashboard. No API key can buy, top up, upgrade or change a payment method: plan, wallet and Fliok invoice reads are open to admin keys with billing:read, and every purchase is made by a signed-in person.
Authorization: Bearer wa_xxxxxxxxxxxxxxxxxxxxxxxxNever expose your API key in client-side code or public repositories. Use environment variables and rotate keys if compromised.
Scopes
A key can also be narrowed to scopes — for example messages:send and templates:read only. The role is the ceiling: a key never gets more than its role allows, and a key with no scopes acts with everything its role allows (every key made before scopes existed). Each endpoint names the scope it needs; a key without it gets 403 insufficient_scope with the missing scope in required. …:write, …:manage and …:delete include …:read of the same thing. The one scope a key below admin can be given is webhooks:manage — the “Zapier / Make / Pabbly triggers” preset — and such a key can only subscribe to events it may read (never invoices or quotations) and only change or remove the endpoints it registered itself. GET /me lists a key's scopes.
Every scope a key can be narrowed to, what it covers, and the lowest role whose keys carry it by default. The same scopes decide which tools an AI app sees over MCP.
| Scope | What it covers | Lowest role |
|---|---|---|
messages:send | Send messages and upload files | agent |
messages:read | Read messages and their delivery status | viewer |
contacts:read | Read contacts, tags and custom fields | viewer |
contacts:write | Add and change contacts, tags, blocks | agent |
contacts:delete | Erase contacts and their history | admin |
conversations:read | Read chats, notes and quick replies | viewer |
conversations:write | Assign, resolve and annotate chats; start chats | agent |
templates:read | Read templates | viewer |
templates:write | Create, delete and sync templates | admin |
broadcasts:read | Read broadcasts, segments and recipients | viewer |
broadcasts:write | Create and cancel broadcasts; manage segments | admin |
webhooks:read | List webhook endpoints (addresses hidden) and events | viewer |
webhooks:manage | Register and manage webhook endpoints | admin |
crm:read | Read leads, tasks and the pipeline | viewer |
crm:write | Add and change leads, notes and tasks | agent |
instagram:read | Read Instagram chats, posts, comments and insights | viewer |
instagram:write | Reply, comment and post on Instagram | agent |
payments:read | Read WhatsApp Pay requests | viewer |
payments:write | Send WhatsApp Pay requests | agent |
analytics:read | Read reports and costs | viewer |
billing:read | Read the plan, wallet and Fliok's invoices | admin |
team:read | Read team members (emails for admin keys only) | viewer |
automations:read | Read flows, bots, automations and auto replies | viewer |
automations:write | Switch automations on and off; start flows | admin |
integrations:read | Read which integrations are connected | viewer |
integrations:manage | Sync, configure and disconnect integrations | admin |
biz:read | Read invoices and bookings of the business modules | viewer |
biz:write | Create and send invoices, record payments, manage bookings | agent |
linkedin:read | Read LinkedIn posts, events, page insights and ad results | viewer |
linkedin:write | Draft, schedule and publish LinkedIn posts and events | agent |
ads:read | Read ads, their results and Lead Ads forms | viewer |
ads:write | Create ads (always paused), pause them, manage Lead Ads | admin |
ads:spend | Publish or resume ads and raise budgets. Spends ad money; never on unless named | admin |
catalog:read | Read the product catalog | viewer |
catalog:write | Add, change, delete and sync catalog products | admin |
Who you send to
A customer may have no phone number. WhatsApp lets people use a username, and then shares no number for them. Their phone is wa:<user id> instead of digits, and webhooks and contacts also carry userId and username. Answer with either field below. A verification-code template needs a real number: to a customer without one, POST /send answers 422 needs_phone_number.
POST /send: to or recipient
Every send names the customer one of these two ways.
| Field | Type | What it is |
|---|---|---|
torequired | string | The customer’s number in E.164, with or without “+”. A number without a country code takes your sending number’s. For a customer WhatsApp shares no number for, pass the phone we report for them ("wa:<user id>") as it is — or leave `to` out and use `recipient`. |
recipient | string | Instead of `to`: the customer’s WhatsApp user id (the `userId` on webhooks and contacts, like "IN.21483648909127364650"), for a customer WhatsApp shares no phone number for. `to` wins when both are sent. |
Returns See Sending for the rest of the body.
Errors
Every error body has an error field. Branch on it, not on the HTTP status or the message, which is written for people and may change. Failures name the cause — outside_24h_window, contact_opted_out, api_key_expired — instead of a generic 400. Both tables are on Errors.
Rate limits
Limits are per-endpoint token buckets scoped to your workspace, and each key may use at most half of any bucket when a workspace has more than one. Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; an empty bucket answers 429 with Retry-After in seconds. Every bucket: Rate limits.
Idempotency
Retries are safe on every write. Send an Idempotency-Key header (1–255 characters, remembered for 24 hours) on any POST, PATCH or DELETE — a message, a lead, a webhook, a secret rotation, a payment voided, a booking completed, an edit or a delete. A payment request is also made safe by your own referenceId. A retry of a finished request answers the first response (with Idempotent-Replayed: true), a retry while it is still running gets 409 idempotency_key_in_progress, and the same key with a different request gets 422 idempotency_key_reused. Keys are kept per API key, so two integrations in one workspace can use the same key without meeting. A request that never finished frees its key after two minutes. Only a success is remembered: after a refusal, fix the request and retry with the same key.
Webhooks and signing
Register a URL and Fliok POSTs each event to it as { id, event, createdAt, data }, signed with HMAC-SHA256 over ${timestamp}.${rawBody} using your endpoint secret, in x-webhook-signature. Delivery is at least once, so de-duplicate on the event id. Non-2xx responses are retried with backoff (12 attempts over about 17 hours), and an endpoint that keeps failing is disabled automatically. The event catalogue and a verification example are on Webhooks.
Versioning
The version is in the path: /api/v1 is the current and only version. Every change to it is dated in the API changelog. Always switch on a webhook’s event and ignore ones you do not handle — new events may be added. The whole surface is also an OpenAPI 3.1 document at /api/v1/openapi.json (no key needed).
Resources
One page per group of endpoints, each with its request bodies, examples and rate limits.
- Sending2Send texts, templates, media and interactive messages, and upload files to send by media id.From a agent key
- Reading6Who is calling, your numbers, and the messages, conversations and contacts in your workspace.From a viewer key
- Templates5List, read, submit, delete and sync your WhatsApp message templates.From a viewer key
- Contacts10Create, update, upsert in bulk, tag, block and delete contacts, and read your tags and custom fields.From a viewer key
- Broadcasts6Send a template to many contacts, preview who it reaches, follow each recipient and cancel.From a viewer key
- Segments5Saved audiences a broadcast can name: list, create, change and delete them.From a viewer key
- Inbox7Read and update a conversation, add notes, mark it read, start a chat and fetch a message’s file.From a viewer key
- Reporting1The analytics the dashboard shows, for a date range.From a viewer key
- Fliok’s invoices to you2The invoices Fliok has issued to your workspace for its plan.From a admin key
- Plan, wallet and costs (read only)3Your plan, wallet balance and what WhatsApp costs you. Read only; admin keys.From a admin key
- Your invoices and bookings32Your own invoices, quotations and bookings: make, issue, send, record payments, reschedule and cancel.From a viewer key
- Automations8List automations, auto replies and bots, switch them on or off, and send a WhatsApp form.From a viewer key
- Team and quick replies5Read your team, and create, change and remove quick replies.From a viewer key
- Webhooks10Register signed webhooks, the events they carry, verifying a signature, deliveries and redelivery.From a viewer key
- Integrations7Read each integration’s status, sync it, change its settings and messages, and disconnect it.From a viewer key
- Sales CRM9Leads, notes, follow-up tasks and the pipeline.From a viewer key
- Instagram13Instagram messages, posts, comments and insights.From a viewer key
- LinkedIn19Draft, schedule and publish LinkedIn posts and events, and read page insights.From a viewer key
- LinkedIn ads35Read LinkedIn ad results and audiences, search the Ad Library, and boost posts.From a viewer key
- Ads22Create ads (always paused), read their results and charges, and manage Facebook Lead Ads forms.From a viewer key
- Product catalog12Read, add, change, delete and sync the products in your Meta catalog.From a viewer key
- WhatsApp Pay2Ask for a payment inside a WhatsApp chat and check whether it was paid.From a viewer key
- Sandbox1Developer sandbox only: make a simulated customer write to your sandbox number.From a agent key
- API description2The OpenAPI 3.1 document describing every endpoint and webhook event.No key needed
Reference: Errors, Rate limits, Webhook events, Developer sandbox, Changelog.
SDKs and code examples
Clients for JavaScript/TypeScript and PHP, each a single file with no dependencies. They send your key, put an Idempotency-Key on every write, wait out a 429’s Retry-After, and verify webhook signatures. Version 0.1.0, not yet on npm or Packagist; download the file and keep it with your code.
/sdk/fliok.jsJavaScript ES module: Node 18+, Bun, Deno, edge runtimes and browsers (any fetch)/sdk/fliok.d.tsIts TypeScript types, kept beside fliok.js/sdk/Fliok.phpPHP 7.4+ with the curl extension
import { Fliok } from './fliok.js';
const fliok = new Fliok({ apiKey: process.env.FLIOK_API_KEY });
// Send an approved template. Fliok builds Meta's components from the variables.
const { messageId } = await fliok.messages.sendTemplate('+919876543210', {
name: 'order_update', language: 'en', variables: ['Yash', 'ORD-12345'],
});
// Handle a webhook (Express). Use the RAW body: re-serialised JSON will not match.
app.post('/webhooks/fliok', express.raw({ type: 'application/json' }), async (req, res) => {
if (!(await Fliok.verifyWebhook(req.body, req.headers, process.env.FLIOK_WEBHOOK_SECRET))) return res.sendStatus(401);
const { id, event, data } = JSON.parse(req.body.toString());
res.sendStatus(200); // then handle `event`; skip an `id` you have already seen.
});<?php
require 'Fliok.php';
$fliok = new \Fliok\Fliok(getenv('FLIOK_API_KEY'));
// Send an approved template. Fliok builds Meta's components from the variables.
$sent = $fliok->sendTemplate('+919876543210', 'order_update', 'en', ['variables' => ['Yash', 'ORD-12345']]);
// Handle a webhook. Use the RAW body: re-encoded JSON will not match.
$raw = file_get_contents('php://input');
if (!\Fliok\Fliok::verifyWebhook($raw, getallheaders(), getenv('FLIOK_WEBHOOK_SECRET'))) { http_response_code(401); exit; }
$delivery = json_decode($raw, true); // ['id' => …, 'event' => …, 'data' => …]
http_response_code(200);Without an SDK
Use any HTTP client in your language. Here are ready-to-use examples for the most popular languages.
const API_KEY = 'wa_your_api_key';
const BASE = 'https://fliok.com/api/v1';
// Send a text message (requires an open 24h window)
const res = await fetch(BASE + '/send', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + API_KEY,
'Content-Type': 'application/json',
'User-Agent': 'my-app/1.0',
// Safe to retry: the same key never sends twice (24 hours)
'Idempotency-Key': 'order-12345-shipped',
},
body: JSON.stringify({
to: '+919876543210',
type: 'text',
text: 'Hello from Fliok!',
}),
});
const data = await res.json();
console.log('Message ID:', data.messageId);import base64, requests
API_KEY = "wa_your_api_key"
BASE = "https://fliok.com/api/v1"
# Name your client: some default library agents (Python's urllib) are refused at the edge.
HEAD = {"Authorization": f"Bearer {API_KEY}", "User-Agent": "my-app/1.0"}
# 1. Upload a PDF -> Meta media id
with open("invoice.pdf", "rb") as f:
up = requests.post(f"{BASE}/media", headers=HEAD, json={
"fileBase64": base64.b64encode(f.read()).decode(),
"mimeType": "application/pdf",
"filename": "invoice.pdf",
}).json()
# 2. Send it
res = requests.post(f"{BASE}/send", headers=HEAD, json={
"to": "+919876543210",
"type": "document",
"payload": {"type": "document",
"document": {"id": up["id"], "filename": "invoice.pdf"}},
})
print("Message ID:", res.json()["messageId"])<?php
$apiKey = 'wa_your_api_key';
$base = 'https://fliok.com/api/v1';
// Send a template (works outside the 24h window)
$ch = curl_init("$base/send");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $apiKey",
"Content-Type: application/json",
"User-Agent: my-app/1.0",
],
CURLOPT_POSTFIELDS => json_encode([
'to' => '+919876543210',
'type' => 'template',
'payload' => [
'type' => 'template',
'template' => [
'name' => 'order_confirmation',
'language' => ['code' => 'en'],
'components' => [[
'type' => 'body',
'parameters' => [
['type' => 'text', 'text' => 'Yash'],
['type' => 'text', 'text' => 'ORD-12345'],
],
]],
],
],
]),
]);
$response = json_decode(curl_exec($ch));
echo "Message ID: " . $response->messageId;Works with any language that supports HTTP requests — Ruby, Go, Java, .NET, Dart, and more.
Ready to Build?
API access is part of the Pro plan, charged when you subscribe, and you create keys from the API keys page.
