Event types
Discover supported events from /protocol.
The event vocabulary is returned by the protocol discovery endpoint. Use it instead of hardcoding assumptions when building an integration.
curl https://api.opendoc.com/protocol | jq '.events.types'
SSE
Use SSE for live agent or dashboard connections.
The SSE stream requires an agent token. Server clients should use Authorization. Browser EventSource clients can pass the token as a query parameter because EventSource does not allow custom headers.
curl https://api.opendoc.com/events \
-H "Authorization: Bearer $OPENDOC_AGENT_TOKEN"
const stream = new EventSource(
"https://api.opendoc.com/events?token=" + encodeURIComponent(agentToken)
);
stream.addEventListener("transaction.state_changed", (event) => {
const payload = JSON.parse(event.data);
console.log(payload);
});
Webhooks
Register a callback for server-to-server delivery.
Webhook subscriptions are owned by the calling agent token. The signing secret is returned once when the subscription is created.
curl https://api.opendoc.com/event-subscriptions \
-X POST \
-H "Authorization: Bearer $OPENDOC_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"callbackUrl": "https://partner.example.com/opendoc/webhooks",
"eventTypes": ["transaction.state_changed", "receipt.finalized"]
}'
Verification
Verify the raw body with HMAC-SHA256.
OpenDoc signs the exact request body and sends the hex digest in x-opendoc-signature. Use the one-time signing secret from subscription creation.
import crypto from "node:crypto";
function verifyOpenDocWebhook(rawBody, signature, signingSecret) {
const expected = crypto
.createHmac("sha256", signingSecret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex")
);
}