Get notified with webhooks
Tell your own systems when members complete quests, earn points or need review, with signed requests.
A webhook lets your own systems hear about member activity as it happens. Use one to:
- Ping your team when a submission needs review.
- Update your CRM when a member completes a quest or earns points.
- Ship a reward your own system fulfils, such as merch, when a member earns it.
You add an endpoint, a web address on your backend, and pick the events it should receive. Domino then sends each matching event there, signed so your backend can tell it came from Domino, and retries when your backend doesn't answer. Your team can see every delivery in Console and send a failed one again.
Manage endpoints in Console
Open Settings → Webhooks in the project and environment you want. Choose Add endpoint, enter the public HTTPS URL, tick the events it should receive, then choose Create endpoint. Copy the one-time signing secret to your backend before leaving the page.
Choose View endpoint to edit the URL or event filters (an event name Console doesn't list stays subscribed when you save), disable or enable delivery, or rotate the signing secret. Its Endpoint ID has a copy button, for the CLI and management.webhooks calls that name the endpoint. The delivery log shows status, attempts, HTTP response and failure reason. After fixing the receiver, enable the endpoint and choose Replay delivery on a completed, failed or skipped delivery. Use Load older deliveries to continue the log and Refresh deliveries to check retry results.
People with read permission can inspect endpoints and deliveries; Console hides mutation controls from them. Changes, secret rotation and replay require publish permission. A pending delivery has no replay action until it finishes. These operational controls work in both Test and Live.
Connect your backend
Create an endpoint
import { createManagementClient, activityTypes } from "@domino-sdk/relay/server"
const management = createManagementClient({
baseUrl: "https://relay.domino.run",
organization: "your-organization",
project: "your-project",
environment: "test",
getToken: async () => "your-management-token",
})
const created = await management.webhooks.create({
url: "https://your-backend.example/webhooks/domino",
types: [activityTypes.completionCreated, activityTypes.reviewPending],
})
const endpointId = created.endpoint.id
const signingSecret = created.secretThe secret is returned only when you create the endpoint or rotate its secret. List responses omit secrets. Keep the endpoint ID for delivery inspection and recovery.
To use the CLI, save the URL and types in endpoint.json, then run:
domino webhooks create endpoint.json --environment test --jsonSave the one-time secret from the output securely. List endpoints with domino webhooks list --json.
Domino refuses an event type it never sends, such as a typo, with unknown-activity-type. The error lists the valid types.
If the answer is lost, for example when the connection drops, do not create the endpoint again. The CLI saves the create as an operation and exits with code 3: run domino operations resume <id> with the ID from the error to get the same endpoint and secret. The saved operation keeps the secret in your local Domino configuration directory only until the CLI has printed it once; after that, rotate the secret if you lost it. From the SDK, call create again with the same actionId.
Verify the raw request
Read the body once as text. Pass the Domino-Signature header and raw body to verifyWebhook before you process the event.
import { verifyWebhook, type WebhookEvent } from "@domino-sdk/relay/server"
export async function receive(
request: Request,
secret: string,
acceptOnce: (event: WebhookEvent) => Promise<void>,
): Promise<Response> {
let event: WebhookEvent
try {
event = await verifyWebhook({
body: await request.text(),
signature: request.headers.get("Domino-Signature") ?? "",
secret,
})
} catch {
return new Response("Invalid webhook", { status: 400 })
}
await acceptOnce(event)
return new Response(null, { status: 204 })
}Branch on event.type, a string, by comparing it with activityTypes, and ignore a type you don't handle instead of failing on it, since Domino adds event types. To type your own handlers, use ActivityType from @domino-sdk/relay/server: it names the known types and stays open to new ones.
Implement acceptOnce with durable storage keyed by event.id. Commit the dedupe record and your business effect in the same transaction, or atomically enqueue durable work. Return a 2xx response only after that commit. Apply a 64 KiB request-body limit in your HTTP server before reading the body.
Rotate the signing secret
Call management.webhooks.rotateSecret(endpointId) or domino webhooks rotate-secret <endpoint> --json. Install the new secret in your receiver immediately. Domino signs with both old and new secrets for 24 hours, so either verifies during deployment. A second rotation during a usable overlap window returns 409. After 24 hours only the new secret signs requests.
If a rotation's answer is lost, resume its CLI operation, or call rotateSecret(endpointId, { actionId }) again with the same actionId. Either returns the same new secret during the overlap. After a later rotation, the earlier one's retry returns 409 secret-superseded.
Recover a failed delivery
- Call
management.webhooks.deliveries(endpointId)ordomino webhooks deliveries <endpoint> --json. Inspectstatus,attempts,responseStatus, andlastError. - Fix the receiver. If the endpoint is disabled, call
management.webhooks.update(endpointId, { enabled: true }). - Call
management.webhooks.replay(endpointId, deliveryId)ordomino webhooks replay <endpoint> <delivery> --json.
Replay uses the original event ID and payload, the current endpoint URL, and the current signing secrets. The new delivery gets its own ID and retry budget. Pending deliveries cannot be replayed. Replaying a successful event still reaches your dedupe check.
If a replay's answer is lost, check the delivery log before replaying again, or pass an action ID up front: domino webhooks replay <endpoint> <delivery> --action-id <uuid>. Sent again with the same ID, the replay returns the delivery it first created instead of creating another.
Pass the returned nextCursor back as cursor (--cursor in the CLI) to continue the delivery log. Endpoint changes and replay are audited. The same delivery diagnostics and replay controls are available in Settings → Webhooks.
See the webhook reference for payload fields, signing format, event types, and limits.
Member activity and event names
The same committed changes can drive a member's cursor-polled activity feed and your backend's webhooks. Member polling works without a webhook endpoint. Subscribe to activityTypes.attemptRejected (attempt.rejected) for rejected attempts, activityTypes.entitlementGranted (entitlement.granted) for new or upgraded earned tiers, and activityTypes.rewardDeliveryUpdated (reward.delivery.updated) for delivery state changes, including Discord reconciliation. Existing completion, review-pending, point-award, collection and referral-qualification events remain available.
Webhook payloads identify the affected member. The member feed explicitly selects public fields and omits review notes, provider diagnostics and the other member's referral identity. Its event names describe member outcomes, such as attempt.accepted and delivery.updated; do not assume they are identical to webhook event names. See Participant activity.
Delete an endpoint
With publish permission, choose Delete endpoint in Console Settings → Webhooks, read its current revision and provide a reason. Deletion stops new delivery and retries, frees endpoint capacity and keeps read-only delivery history under Deleted endpoints. An HTTP request already in flight may finish. Deleted endpoints cannot be resumed, rotated or replayed; create a new endpoint to send again.
Wallet sign-in in your own Next.js app
Sign members in with Sign-In with Ethereum or Solana in an app you host, and develop it on localhost against real Domino.
Connect Discord and X
Connect your Discord server so quests can check who joined, reward messages and reactions, and hand out roles. Check follows, posts and profiles on X.