Webhook-First Inbound Integration Checklist for UnifyPort
If you are building an inbound messaging integration, create and verify the webhook before you connect production accounts. UnifyPort has no general REST message-history read API or guaranteed replay for missed payloads, so the webhook is your durable intake layer. Register POST /v1/webhook-endpoints, enable signing_secret, subscribe to message.received or [*], store each event, then route it to support, CRM, AI, or automation tools.
Key takeaways
- Register the webhook endpoint first; do not treat it as the last setup step.
- Use
signing_secretso deliveries includeX-Device-TimestampandX-Device-Signature. - Store the standard event envelope before running slow downstream work.
- For an inbox-only service, subscribe to
message.receivedand filter ondata.message.direction === "inbound". - Keep endpoint filtering, signature verification, retry handling, and business routing as separate concerns.
Why the webhook comes first
A messaging account can receive a customer message before your CRM, AI agent, or shared inbox is ready. If the receiver is not registered, that event is not something you can reliably fetch later through a message-history API. This is why the UnifyPort Quickstart registers a webhook before account authorization.
This article is the operational companion to two deeper tutorials: Webhook HMAC replay protection covers timestamp freshness and idempotency, while UnifyPort webhook event filters explains when to use explicit subscribed_events versus the wildcard. Here we put the sequence together as a first integration checklist.
Step 1: create one signed endpoint
The API route is POST /v1/webhook-endpoints. A focused inbound receiver can start with message.received; a general event collector can use [*].
curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
-H "X-Api-Key: $UNIFYPORT_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"url\": \"$PUBLIC_WEBHOOK_URL\",
\"status\": \"active\",
\"subscribed_events\": [\"message.received\"],
\"signing_secret\": \"$WEBHOOK_SIGNING_SECRET\",
\"retry_policy\": { \"max_attempts\": 3 }
}"
retry_policy.max_attempts is the number of retries after the initial delivery. The documented default is 3, and the accepted range is 0 to 5. Open the Create webhook endpoint reference when you wire this into your own provisioning flow.
Step 2: verify the raw delivery
When signing is enabled, UnifyPort sends X-Device-Signature, a hex HMAC-SHA256 over this exact value:
<X-Device-Timestamp>.<raw request body>
The receiver must verify the raw bytes before JSON parsing or re-serialization. Node.js provides crypto.createHmac() and crypto.timingSafeEqual() for this pattern; the official Node.js documentation notes that equal-length buffers are required for timingSafeEqual().
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;
app.post('/webhooks/unifyport', express.raw({ type: 'application/json' }), async (req, res) => {
const timestamp = req.get('X-Device-Timestamp') ?? '';
const signature = req.get('X-Device-Signature') ?? '';
const expected = crypto
.createHmac('sha256', signingSecret)
.update(timestamp + '.')
.update(req.body)
.digest('hex');
const valid =
/^[0-9a-f]{64}$/i.test(signature) &&
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) return res.sendStatus(401);
const event = JSON.parse(req.body.toString('utf8'));
await inbox.insertIfAbsent(event.id, event);
return res.sendStatus(202);
});
For a production receiver, add timestamp freshness, durable idempotency, and retry-aware acknowledgement as described in the Webhook delivery and signature verification guide.
Step 3: store the standard event envelope
A message.received delivery has the same top-level shape across providers:
{
"id": "evt_2f9c1a4b7e",
"type": "message.received",
"provider": "whatsapp",
"account_id": "acc_8c21d0",
"occurred_at": "2026-06-08T12:34:56Z",
"data": {
"conversation": { "id": "8613912345678", "type": "user", "title": "Jordan Lee" },
"sender": { "id": "8613912345678", "name": "Jordan Lee", "type": "user" },
"message": {
"id": "wamid.HBgM",
"text": "Hi - is my order shipped yet?",
"direction": "inbound",
"sent_at": "2026-06-08T12:34:55Z"
}
}
}
Store at least id, type, provider, account_id, occurred_at, data.conversation.id, data.sender.id, and data.message.id. Then your workers can safely update a shared inbox, call an AI model, notify Slack, or send a reply. If you use n8n after the edge, the n8n WhatsApp AI agent tutorial shows why the workflow should receive a trusted event after verification rather than own the first security boundary.
Limitations and trade-offs
An unofficial interface is useful when your team needs inbound messages from ordinary or existing messaging accounts, but it does not replace an official provider API when you need platform certification, official business features, or provider-specific policy guarantees. Also remember that message.received can describe inbound or outbound traffic, so an inbox pipeline must inspect data.message.direction.
FAQ
Which webhook subscription should I start with?
Use message.received for an inbound inbox. Use [*] only when the endpoint is a general collector and downstream consumers can tolerate every public standard event type.
Is the webhook signature enough for replay protection?
No. HMAC verifies integrity and knowledge of the shared secret. Add timestamp freshness and durable deduplication by event ID.
Should I return 200 before the CRM write finishes?
Return 2xx after the event is durably accepted into your own inbox or queue. Slow CRM writes and AI work should happen asynchronously.
Can I add LINE, Zalo, or X later?
Yes. The same envelope includes provider and account_id, so you can branch downstream while keeping one signed intake path.
Next step
Open the Create webhook endpoint reference, register the receiver, then continue with the webhook delivery guide before connecting production messaging accounts.
Sources
Official sources checked on 2026-08-26:
Turn messaging integration into a stable product pipeline.
Start by sending through one API, then bring every inbound message back into your business system with standard events.