Search for a command to run...
Onboard your clients' WhatsApp Business numbers through an Intelli-hosted flow — you never touch Meta credentials.
You only ever need your API key.
Intelli hosts the Meta signup popup on its own domain. You don't register a Meta app, you don't handle a Meta App ID, Config ID, or access token, and no Meta credential ever reaches your frontend. Your server holds a single secret: your ik_ key.
Copy the integration contract into your assistant, then let it tailor the implementation to your stack.
You are helping me integrate WhatsApp into my product using the Intelli Partner API (base URL: https://api.intelliconcierge.dev/v1). Intelli hosts Meta's embedded signup, so my app never touches Meta credentials — everything goes through my Intelli API key.
Before writing code, ask me:
1. My backend framework/language (and frontend framework if relevant).
2. Whether the hosted signup should open as a popup or a full redirect.
Environment variables (server-side only — the API key must NEVER reach the browser):
- INTELLI_API_KEY: my secret key. ik_test_... keys validate everything but never deliver; ik_live_... delivers for real.
- INTELLI_WEBHOOK_SECRET: HMAC secret for verifying incoming webhooks (from the Intelli portal, Webhooks section).
Every API call: Authorization: Bearer <INTELLI_API_KEY>. Errors are JSON {"error": {"code": "...", "message": "...", "details": {...}}}.
Implement:
A) Client onboarding (Intelli-hosted embedded signup)
1. A server endpoint that creates a signup session:
POST /embedded-signup/sessions with body {"client_ref": "<my unique id for this customer>"}
-> 201 {"session_id": "...", "url": "https://...", "expires_in": 600}
Sessions are single-use, valid 10 minutes. Handle 409 code "number_already_connected": that WhatsApp number is already connected (as one of my clients, or through another provider) — show the customer a clear message.
2. Frontend: open the returned url in a popup (about 600x720). Listen for a window "message" event where event.data = {type: "intelli:whatsapp", status: "success", client_ref: "..."}. Treat it as a hint only — it is untrusted.
3. Server-side confirmation: GET /clients/<client_ref>
-> 200 {client_ref, channel, waba_id, phone_number_id, phone_number, business_name}
Only mark the customer as connected once this succeeds. Persist client_ref — it identifies this customer in every later call.
4. Instagram variant: POST /instagram-connect/sessions works identically (same body, same 3 steps; completion message type is "intelli:instagram"). The resulting client has channel "instagram": "to" is the Instagram-scoped user ID from webhook events, and it carries text plus image/video/audio/document — media must be a public HTTPS URL in "link" (an uploaded media "id" returns "media_link_required"), and "caption"/"filename" come back under "warnings" because an Instagram attachment has nowhere to put them. Templates, sticker, location, contacts, interactive and reaction are WhatsApp-only and return "channel_type_unsupported".
B) Sending messages (server-side)
- POST /messages/send mirrors Meta's Cloud API: {"client_ref": "...", "to": "<E.164 digits, no +>", "type": "<t>", "<t>": {...}} where <t> is one of text, image, video, audio, document, sticker, location, contacts, interactive, reaction. Examples: text needs {"body": "..."}; media types need {"link": "<public https url>"} (or an uploaded media "id"), image/video/document accept "caption", document accepts "filename"; location needs {latitude, longitude}; interactive needs Meta's interactive object ({"type": "button" | "list" | ...}); reaction needs {message_id, emoji} (empty emoji removes it).
- Template: POST /messages/send-template with {"client_ref": "...", "to": "...", "template": {"name": "...", "language": {"code": "en_US"}, "components": [...]}}
- Free-form types require an open 24h customer-service window; outside it only templates deliver.
- Success: {"success": true, "message_id": "wamid..."}. With a test key the response includes "dry_run": true and nothing is delivered.
C) Webhook receiver
- A public POST endpoint in my app; I register its URL in the Intelli portal (Webhooks section).
- Verify the X-Intelli-Signature header: "sha256=" + hex(HMAC_SHA256(INTELLI_WEBHOOK_SECRET, raw request body)), constant-time comparison, reject mismatches.
- Payload: {"event": "...", "timestamp": "...", "client_ref": "...", "phone_number": "...", ...}. Event types: message.received (has messages[]), message.status (has statuses[]), message.reaction, template.status, user.preferences.
- Respond 2xx immediately and process asynchronously; failed deliveries are retried with backoff, and events can be redelivered manually — dedupe by message id.
D) Error handling
- 401 invalid/revoked key, 403 missing scope, 404 unknown client_ref, 409 number_already_connected, 429 rate-limited (respect Retry-After), 402 subscription required.
Start with my ik_test_ key end-to-end, then switch to live. Write idiomatic, production-quality code for my stack.Call from your server with your API key. client_ref is your own identifier for this client (unique within your account). Requires the clients:write scope.
POST https://api.intelliconcierge.dev/v1/embedded-signup/sessions
Authorization: Bearer ik_live_...
Content-Type: application/json
{
"client_ref": "your-internal-client-id"
}{
"session_id": "a7jBeKh4uW97sypQ...",
"url": "https://www.intelliconcierge.dev/embed/whatsapp?session=a7jBeKh4uW97sypQ...",
"expires_in": 600
}The session is single-use and expires in 10 minutes.
Open url in a popup (recommended) or redirect your client to it. When opened as a popup, the page posts a message back to the opener when the flow completes:
const popup = window.open(session.url, "intelli-whatsapp", "width=600,height=720");
window.addEventListener("message", (event) => {
if (event.data?.type === "intelli:whatsapp" && event.data?.status === "success") {
// The number is connected. Confirm it from your server (step 3).
finishOnboarding(event.data.client_ref);
}
});The message only signals completion — treat it as untrusted and always confirm the result server-to-server in step 3.
Fetch the onboarded client from your server with your API key. Requires the clients:read scope.
GET https://api.intelliconcierge.dev/v1/clients/your-internal-client-id Authorization: Bearer ik_live_...
{
"client_ref": "your-internal-client-id",
"waba_id": "123456789",
"phone_number_id": "987654321",
"phone_number": "+1234567890",
"business_name": "Client Business"
}Store the client_ref — you'll use it to send messages and to match incoming webhooks for this client.
The same three-step contract connects an Instagram professional account. Create the session on a different endpoint; everything else — opening the URL, the completion message (type intelli:instagram), and the server-side confirmation via GET /clients/<client_ref> — works identically.
POST https://api.intelliconcierge.dev/v1/instagram-connect/sessions
Authorization: Bearer ik_live_...
Content-Type: application/json
{
"client_ref": "your-internal-client-id"
}Connected clients carry channel: "instagram" in the clients API, webhook events include the same field, and sends route automatically. Instagram carries text plus image, video, audio and document — media as a public HTTPS URL in link, since Instagram fetches the file itself. Templates are WhatsApp-only.
Copy the integration contract into your assistant, then let it tailor the implementation to your stack.
You are helping me integrate Instagram DMs into my product using the Intelli Partner API (base URL: https://api.intelliconcierge.dev/v1). Intelli hosts the Instagram connect flow, so my app never touches Meta credentials — everything goes through my Intelli API key.
Before writing code, ask me:
1. My backend framework/language (and frontend framework if relevant).
2. Whether the hosted connect should open as a popup or a full redirect.
Environment variables (server-side only — the API key must NEVER reach the browser):
- INTELLI_API_KEY: my secret key. ik_test_... keys validate everything but never deliver; ik_live_... delivers for real.
- INTELLI_WEBHOOK_SECRET: HMAC secret for verifying incoming webhooks (from the Intelli portal, Webhooks section).
Every API call: Authorization: Bearer <INTELLI_API_KEY>. Errors are JSON {"error": {"code": "...", "message": "...", "details": {...}}}.
Implement:
A) Client onboarding (Intelli-hosted Instagram connect)
1. A server endpoint that creates a connect session:
POST /instagram-connect/sessions with body {"client_ref": "<my unique id for this customer>"}
-> 201 {"session_id": "...", "url": "https://...", "expires_in": 600}
Sessions are single-use, valid 10 minutes. Handle 409 code "account_already_connected": that Instagram account is already connected (as one of my clients, or through another provider) — show the customer a clear message.
2. Frontend: open the returned url in a popup (about 600x720). The page walks the customer through Instagram's login/consent, then posts a window "message" event where event.data = {type: "intelli:instagram", status: "success", client_ref: "..."}. Treat it as a hint only — it is untrusted.
3. Server-side confirmation: GET /clients/<client_ref>
-> 200 {client_ref, channel: "instagram", business_name, ...}
Only mark the customer as connected once this succeeds. Persist client_ref.
B) Sending messages (server-side)
- POST /messages/send with {"client_ref": "...", "to": "<Instagram-scoped user ID>", "type": "text", "text": {"body": "..."}}
- "to" is the IGSID that arrives in webhook events (sender.id) — you cannot derive it from a username. Text is capped at 1000 characters.
- Media: types "image", "video", "audio" and "document" also work. Pass a public HTTPS URL, e.g. {"type": "image", "image": {"link": "https://..."}}. Instagram fetches the file itself, so an uploaded media "id" is rejected with code "media_link_required". A "caption" or "filename" has nowhere to go on an Instagram attachment: the send succeeds and the response lists it under "warnings" — send the caption as its own text message if you need it.
- Not available on Instagram (code "channel_type_unsupported"): templates, sticker, location, contacts, interactive (buttons/lists) and reaction.
- Instagram allows replies within 24 hours of the customer's last message — send outside that window and Meta rejects the call.
- Success: {"success": true, "message_id": "..."}, plus "warnings": [...] when a field could not be carried. With a test key the response includes "dry_run": true and nothing is delivered — a test key rejects exactly what a live key would.
C) Webhook receiver
- A public POST endpoint in my app; I register its URL in the Intelli portal (Webhooks section).
- Verify the X-Intelli-Signature header: "sha256=" + hex(HMAC_SHA256(INTELLI_WEBHOOK_SECRET, raw request body)), constant-time comparison, reject mismatches.
- Payload: {"event": "...", "channel": "instagram", "timestamp": "...", "client_ref": "...", ...}. Event types: message.received (has messages[] — each with sender.id = the IGSID to reply to), message.echo, message.reaction, message.read, message.postback.
- Respond 2xx immediately and process asynchronously; failed deliveries are retried with backoff, and events can be redelivered manually — dedupe by message id.
D) Error handling
- 401 invalid/revoked key, 403 missing scope, 404 unknown client_ref, 409 account_already_connected, 429 rate-limited (respect Retry-After), 402 subscription required, 400 channel_type_unsupported (type the channel does not carry), 400 media_link_required (media sent as an id instead of a URL).
Start with my ik_test_ key end-to-end, then switch to live. Write idiomatic, production-quality code for my stack.With a connected client you can send messages and receive webhooks on their behalf.