Send a message
to, messaging_type, and message.
messaging_type
Mirrors Meta’s Messenger Platform messaging type and controls when a send is allowed:
message
The message object carries the content. Provide either text or an attachment:
tag (required when messaging_type is MESSAGE_TAG), metadata (a string→string map), and scheduled_at (ISO-8601 timestamp). There is no top-level type, text, or from field — the connected Instagram account is resolved from your API key.
Generic templates
Instagram’s only structured outbound payload is the generic template — a horizontally scrollable carousel of cards, each with an image, title, subtitle, and buttons. Button and receipt templates are Messenger-only and are not supported on Instagram. Becausemetadata is a string→string map, a generic template is requested through two metadata keys rather than a dedicated message shape:
elements is missing or is not valid JSON, the send falls back to the plain message.text body.
Response
Capabilities
- Text + media — text, image, video, audio, file attachments (up to Meta’s per-type size caps).
- Quick-reply postbacks (inbound) — when a user taps a quick-reply button you presented to them, the tap is delivered as a
message.receivedwebhook event withquick_reply.payloadpopulated. Attaching quick-reply buttons to an outbound DM is not currently supported by the send API. - Generic templates — multi-card carousels with image, title, subtitle, and buttons. Triggered on a send via
metadatafields — see Generic templates below. - 24-hour messaging window — inside the standard messaging window, replies are free; outside the window, only
MESSAGE_TAGS(HUMAN_AGENT, ACCOUNT_UPDATE, POST_PURCHASE_UPDATE, CONFIRMED_EVENT_UPDATE) can be used. - Story mention + story reply — delivered as standard
message.receivedwebhook events. A story mention carriesmetadata.story_mention.url; a story reply carriesmetadata.story_reply(urland/orid). There are no dedicatedinstagram.story.*event types — subscribe tomessage.receivedand branch on themetadatafields. - Inbound webhooks — DMs and postbacks are POSTed to your registered webhook endpoint. Because Instagram messaging runs on Meta’s platform, these callbacks are signed with Meta’s
X-Hub-Signature-256header (the same scheme as WhatsApp), not the Standard Webhooks / Svix scheme Orbit uses for its own outbound webhooks. The header value issha256=<hex>, where<hex>isHMAC-SHA256(rawBody)keyed with your Meta app secret. Verify it by recomputing the HMAC over the raw request body and comparing with a timing-safe check; requests with a missing or invalid signature are rejected with401 INVALID_SIGNATURE.
Rate limits
Meta enforces a 200 DM / hour limit per Instagram account (reduced from 5000 in February 2026). Orbit runs a per-tenant, Redis-backed sliding-window counter ahead of every send so you stay under Meta’s quota and avoidRATE_LIMIT_EXCEEDED (429) responses and account restrictions. When a send would exceed the window it is rejected with RATE_LIMIT_EXCEEDED before it ever reaches Meta.
The hourly ceiling is operator-tunable without a redeploy — set the env var (Secret Manager / cluster env) and restart the pod:
Onboarding flow
- From the dashboard, navigate to Channels → Instagram.
- Click Connect Instagram and authorise via Meta’s OAuth dialog. The dialog requests
instagram_basic,instagram_manage_messages,pages_messaging, andpages_show_listscopes. - Select the Instagram Business / Creator account you want to attach.
- Verify the inbound webhook subscription registered automatically; the webhook URL points to
https://api.orbit.devotel.io/api/v1/webhooks/inbound/instagram.
Common errors
Theerror.code you can branch on is one of three values. Meta-side rejections (expired token, outside the messaging window, non-Business account, opted-out recipient) all surface as MESSAGE_SEND_FAILED — Orbit does not re-expose Meta’s raw error string, so inspect details.provider_status for the upstream HTTP status to tell those cases apart.