Connecting an External Agent to Meta-AI

Guide for any AI agent or automation (Claude Agent SDK, n8n, Zapier, custom script, etc.) that needs to send WhatsApp/Messenger/Instagram messages or read analytics from the meta-ai platform.

There are two integration paths:

PathBest forSetup time
REST API with X-API-KeyAny HTTP-capable client. Custom scripts, n8n, Zapier, server-side workflows.2 min
MCP serverClaude Desktop, other MCP-aware clients. Tools appear natively in the chat.5 min

Both paths use the same API key.


Get an API Key

  1. Log in at https://meta.agenticlabs.site as an owner or admin of your tenant.
    (Members and viewers can't create keys — ask an admin.)

  2. Settings → API Keys.

  3. Click Create key.

  4. Give it a name (e.g. n8n production, claude-desktop), optional expiry, and select the permissions you need:

    ScopeAllows
    messages:sendPOST /api/messages/send
    messages:readinbox contacts, conversation history, message stats
    analytics:readsocial analytics (IG/FB), ads analytics
    templates:readlist WhatsApp + Messenger templates

    Pick the minimum the agent needs.

  5. Copy the full key shown in the dialog. It's only displayed once. Format: mta_<40-char-alphanum>.

  6. Store it as a secret in your agent system. Revoke from the same page if it ever leaks.


1. REST API

Base URL: https://api-meta.agenticlabs.site
Header: X-API-Key: mta_...
Interactive docs: https://api-meta.agenticlabs.site/docs (Swagger UI)

Minimal curl test

export KEY="mta_yourkeyhere"
curl -sS -H "X-API-Key: $KEY" \
  "https://api-meta.agenticlabs.site/api/messages/inbox/contacts?tenant_id=YOUR_TENANT_UUID"

Tenant ID: visible in the URL of your dashboard, or returned in account_membership.tenantId after login.

Allowlisted endpoints

OperationMethod + pathScope
Send a messagePOST /api/messages/sendmessages:send
Inbox contactsGET /api/messages/inbox/contacts?tenant_id=…messages:read
Message statsGET /api/messages/stats?days=7messages:read
Inbox messagesGET /api/messages/inbox?tenant_id=…messages:read
User profileGET /api/messages/user-profile/{channel_id}/{user_id}messages:read
Conversation historyGET /api/conversations/history/by-channel/{channel_id}/{user_id}messages:read
Social summary (IG + FB)GET /api/social/summaryanalytics:read
IG postsGET /api/social/instagram/posts?limit=12analytics:read
FB postsGET /api/social/facebook/posts?limit=12analytics:read
Ad accountsGET /api/ads/accountsanalytics:read
Ad account summaryGET /api/ads/accounts/{id}/summary?date_preset=last_7danalytics:read
...and the rest of /api/ads/* and /api/social/*analytics:read

Routes not in this list (OAuth, team management, billing, channel deletion) require a human JWT — API keys won't work there by design.

Sending a message

POST /api/messages/send. Body shape depends on type:

WhatsApp — text

curl -X POST https://api-meta.agenticlabs.site/api/messages/send \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "channel_id": "CHANNEL_UUID",
    "to": "5215512345678",
    "type": "text",
    "text": "Hello from my agent"
  }'

WhatsApp — pre-approved template

curl -X POST https://api-meta.agenticlabs.site/api/messages/send \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "channel_id": "CHANNEL_UUID",
    "to": "5215512345678",
    "type": "template",
    "template_name": "hello_world",
    "language_code": "en_US"
  }'

Messenger — button template

curl -X POST https://api-meta.agenticlabs.site/api/messages/send \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "channel_id": "CHANNEL_UUID",
    "to": "USER_PSID",
    "type": "button_template",
    "text": "Pick one:",
    "template_buttons": [
      {"type": "postback", "title": "Option A", "payload": "opt_a"},
      {"type": "web_url",  "title": "Open link", "url": "https://example.com"}
    ]
  }'

Instagram — reaction

curl -X POST https://api-meta.agenticlabs.site/api/messages/send \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{
    "channel_id": "CHANNEL_UUID",
    "to": "USER_IGSID",
    "type": "reaction",
    "message_id": "WAMID...",
    "emoji": "❤️"
  }'

For the full list of supported type values and per-type fields, see the Swagger schema at /docs or frontend/lib/api/client.ts in this repo.

Discovering channel IDs

# Requires JWT (admin route). One-time: log in via the UI, copy your JWT
# from the network tab or call /api/auth/login.
curl -H "Authorization: Bearer $JWT" \
  "https://api-meta.agenticlabs.site/api/auth/meta/channels?tenant_id=YOUR_TENANT_UUID"

Returns an array of {id, channel_type, channel_name, meta_page_id, ig_account_id, phone_number_id, disconnected_at}. Use id as channel_id in send calls. (Future improvement: expose a key-callable read-only GET /api/channels — tracked in the Mission Control epic.)

Errors

StatusMeaning
401Missing/invalid/revoked/expired key
403Key lacks the required scope, OR the requested tenant/channel doesn't belong to this key's tenant
404Channel not found
400 / 422Bad request body (read detail in the JSON response)
5xxServer error — check the Status page and retry with backoff

last_used_at on your key updates on every successful call — useful for spotting unused or compromised keys in the UI.

Node / fetch example

const KEY = process.env.META_AI_API_KEY!;
const BASE = 'https://api-meta.agenticlabs.site';

async function api(path: string, init: RequestInit = {}) {
  const res = await fetch(`${BASE}${path}`, {
    ...init,
    headers: {
      'X-API-Key': KEY,
      'Content-Type': 'application/json',
      ...(init.headers || {}),
    },
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
}

await api('/api/messages/send', {
  method: 'POST',
  body: JSON.stringify({
    channel_id: 'CHANNEL_UUID',
    to: '5215512345678',
    type: 'text',
    text: 'Hello from Node',
  }),
});

Python example

import os, requests
KEY = os.environ["META_AI_API_KEY"]
BASE = "https://api-meta.agenticlabs.site"
s = requests.Session()
s.headers.update({"X-API-Key": KEY})

r = s.post(f"{BASE}/api/messages/send", json={
    "channel_id": "CHANNEL_UUID",
    "to": "5215512345678",
    "type": "text",
    "text": "Hello from Python",
})
r.raise_for_status()
print(r.json())

2. MCP Server (for Claude Desktop)

If you're using Claude Desktop or another MCP-aware client, install our MCP server. It exposes the REST operations above as native MCP tools so Claude can call them with structured arguments.

See mcp-server/README.md for full install + config. TL;DR for Claude Desktop:

git clone https://github.com/SandPalace/meta-ai.git
cd meta-ai/mcp-server && npm install && npm run build

Then add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "meta-ai": {
      "command": "node",
      "args": ["/absolute/path/to/meta-ai/mcp-server/dist/index.js"],
      "env": {
        "META_AI_API_KEY": "mta_..."
      }
    }
  }
}

Restart Claude Desktop. The tools (send_message, list_inbox_contacts, get_conversation_history, get_social_summary, etc. — 10 total) will appear in the tools list.


Security & Best Practices

  • Treat API keys like passwords. Never commit them, never log them.
  • Use the minimum scopes. If your agent only reads, don't grant messages:send.
  • Set an expiration for short-lived integrations (demos, testing).
  • Rotate periodically. Revoke and create a new key every quarter for production keys.
  • One key per workflow. Don't share a single key across many agents — separate keys make it easy to revoke one without breaking others, and the last_used_at audit becomes meaningful.
  • HTTPS only. Our API rejects HTTP. Your client must trust the agenticlabs.site certificate chain (standard).

Known limits (today)

  • No rate limiting yet. A leaked key has unlimited blast radius until revoked. Tracked in Mission Control: rate-limit story under the "API key & external integration hardening" epic.
  • No per-route fine-grained scopes beyond the 4 above. analytics:read grants both social and ads. Splitting into social:read + ads:read is tracked in the same epic.
  • No usage audit log beyond last_used_at. A /recent activity view per key is on the roadmap.
  • MCP server isn't published to npm yet — install from source for now.

If any of these are blockers for your use case, open an issue.