Documentation Index
Fetch the complete documentation index at: https://docs.arcuserp.com/llms.txt
Use this file to discover all available pages before exploring further.
What you will build
A local HTTP server that receives Arcus webhook events, verifies the signature, and logs the event type. You will then trigger a real event from the Arcus app and see it arrive.
Prerequisites
- An API key with
webhooks:write scope
- Node.js 18 or later (or adapt the examples to your stack)
- ngrok or similar tunnel (to expose localhost to the internet)
Step 1: Start a local server
// server.js
import express from 'express';
import crypto from 'crypto';
const app = express();
const WEBHOOK_SECRET = process.env.ARCUS_WEBHOOK_SECRET;
// Use raw body for signature verification
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['arcus-signature'];
if (!signature) {
return res.status(400).send('Missing signature');
}
// Verify the signature
const [tPart, vPart] = signature.split(',');
const timestamp = tPart.replace('t=', '');
const receivedSig = vPart.replace('v1=', '');
const payload = `${timestamp}.${req.body.toString()}`;
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(receivedSig))) {
return res.status(400).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log(`Received event: ${event.type} (${event.id})`);
console.log(JSON.stringify(event.data.object, null, 2));
res.status(200).json({ received: true });
});
app.listen(4242, () => console.log('Listening on :4242'));
ARCUS_WEBHOOK_SECRET=whsec_test_placeholder node server.js
Step 2: Expose with ngrok
Copy the https:// forwarding URL (e.g. https://abc123.ngrok-free.app).
Step 3: Register the endpoint
curl -X POST https://api.arcuserp.com/v1/entities/$ARCUS_ENTITY_ID/webhooks \
-H "Authorization: Bearer $ARCUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://abc123.ngrok-free.app/webhook",
"events": ["order.confirmed", "order.created"],
"description": "Local dev webhook"
}'
Save the webhook_secret from the response:
export ARCUS_WEBHOOK_SECRET=whsec_...
Restart your server with the real secret.
Step 4: Trigger an event
In the Arcus app, create a new draft order and confirm it. Within a few seconds, your terminal should show:
Received event: order.confirmed (evt_01H...)
{
"id": "ord_01H...",
"object": "order",
"document_type": "sales_order",
"status": "confirmed",
...
}
Step 5: Handle events safely
In production, follow these patterns:
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
// ... verify signature (same as above) ...
const event = JSON.parse(req.body);
// 1. Respond immediately -- don't block on processing
res.status(200).json({ received: true });
// 2. Deduplicate using event.id
const alreadyProcessed = await db.webhookEvents.findUnique({ where: { id: event.id } });
if (alreadyProcessed) return;
await db.webhookEvents.create({ data: { id: event.id } });
// 3. Process asynchronously
await queue.enqueue({ type: event.type, data: event.data.object });
});
Next steps
- Read Webhooks for the full event catalog and retry behavior
- Add more event types to your subscription in Settings > Developers > Webhooks
- Set up monitoring to alert on delivery failures (Arcus retries 6 times over 24 hours)