Webhooks
Receive signed market, asset and portfolio insight notifications and retrieve the published content.
Subscribe to assessment.published to receive notifications about eligible new market, asset and portfolio assessments. Motif posts to your backend; your backend verifies the notification and reads the published content through the API. Start with Integrate insights into your app for the complete integration.
Motif must enable event-driven assessment publication in your environment. A subscription does not enable it automatically. Notifications are sent after eligible event-driven research is published; they are not receipts for every data upload, calculation, scheduled refresh or card recomposition. Owner-scoped private assessments are not sent to organization webhooks.
Register your endpoint
Use Settings → Webhooks, or choose your backend integration below. The SDK examples require the SDK version described in release coverage and configured motif client from Integrate insights into your app. REST paths are relative to your API base URL.
POST /v1/sdk/webhooks
Content-Type: application/json
x-api-key: <motif-api-key>
{
"name": "Partner insights",
"url": "https://partner.example.com/webhooks/motif",
"events": ["assessment.published"]
}Inspect schemas in Swagger UI for sandbox or production, or import the OpenAPI JSON for sandbox or production. Swagger requests target the environment you opened.
const subscription = await motif.webhooks.create({
name: 'Partner insights',
url: 'https://partner.example.com/webhooks/motif',
events: ['assessment.published'],
})Save subscription.id and subscription.secret securely before testing.
Replace the example URL with your public HTTPS receiver on port 443. Private network addresses and redirects are not supported. Save the returned endpoint id and signing secret (whsec_…). The secret is shown only at creation or rotation.
GET
/v1/sdk/webhooks
List subscriptions in your organization
POST
/v1/sdk/webhooks
Create a subscription
PATCH
/v1/sdk/webhooks/{id}
Change URL, events or enabled status
DELETE
/v1/sdk/webhooks/{id}
Delete the subscription and its delivery history
POST
/v1/sdk/webhooks/{id}/rotate-secret
Issue a new secret; the old one stops working immediately
POST
/v1/sdk/webhooks/{id}/test
Send a signed webhook.test request
GET
/v1/sdk/webhooks/{id}/deliveries
Inspect attempts; supports limit (1–100) and cursor
Test the saved subscription and inspect delivery attempts:
Set MOTIF_WEBHOOK_ID to the returned endpoint ID and use the configured base URL and API key:
The test endpoint returns { success, statusCode, duration, error }. Require success: true and a 2xx receiver status; the API can return HTTP 200 with success: false. Confirm the receiver recorded the test as well.
A test checks connectivity and signing. It does not generate an insight and has no assessment event ID. After it succeeds, verify real publications for all three surfaces in the agreed pilot window.
Create the subscription once and persist its ID. After a lost create response, reconcile GET /v1/sdk/webhooks by endpoint URL/name before creating another. The secret cannot be recovered from that list; rotate it if the original response was lost.
Publication payload
Illustrative portfolio notification:
Treat id as opaque. Use it for deduplication; data.revision is an assessment revision and is independent of your portfolio snapshot revision.
data.surface
subjectId
portfolioId
Subscription scope
MARKET
GLOBAL for the shared market assessment
null
Enabled subscribing organizations
ASSET
Motif asset ID
null
Organizations with an active portfolio holding the asset, subject to the assessment's scope
PORTFOLIO
Motif portfolio ID
Same portfolio ID
The organization owning that active portfolio
An asset notification represents the asset-level read; it is not sent once per holding. Use your asset-to-portfolio mapping to identify affected books. Notifications contain metadata, not the full prose or evidence. They do not carry your external portfolio ID.
Verify each request
Motif sends JSON with these headers:
X-Webhook-Event
assessment.published, or webhook.test for a test
X-Webhook-Id
Registered endpoint ID
X-Webhook-Event-Id
Publication event ID; matches payload id, absent on tests
X-Webhook-Timestamp
Signing time as Unix seconds; distinct from the payload's publication time
X-Webhook-Signature
v1= followed by the HMAC-SHA256 signature
For a receiver that does not use the SDK, reproduce the current signing contract exactly:
The derived signing key is the UTF-8 hex string, not decoded hash bytes. Reject missing or malformed headers and timestamps outside the accepted tolerance; compare signatures in constant time. Do not sign re-serialized JSON or use the displayed secret directly as the final HMAC key. Test requests use the same signature format.
A Node.js implementation using the original body buffer:
This function verifies authenticity and timestamp freshness. Your receiver still validates the payload, expected organization/endpoint and event uniqueness before processing it.
Use a released SDK version containing verifyWebhookSignature. Pass the original UTF-8 request body before JSON parsing, the two signature headers and the displayed secret. The helper checks the signature and a five-minute timestamp tolerance by default:
Capture the raw bytes before JSON middleware runs. Header names are case-insensitive; reject repeated or missing signature/timestamp headers. Select the stored secret by the registered endpoint ID.
After signature verification, branch on the event type before validating an assessment. The test body has a different schema:
A test has no id, version or organizationId. Check data.webhookId against X-Webhook-Id and your endpoint configuration, record the test and return 2xx.
For an assessment, validate version 1, the expected organization, positive integer revision and the documented surface/subject relationship. Check endpoint/event headers and X-Webhook-Event-Id === body.id. Keep the receiver's clock synchronized.
Acknowledge and retrieve the insight
Durably save or queue the verified event before returning 2xx, within ten seconds. Use a unique (organizationId, eventId) record to deduplicate publications and process the insight asynchronously. If durable storage fails, return 5xx; if processing fails after acknowledgment, your own worker must retry it. If the event is already saved, acknowledge the duplicate successfully.
Use the event's language and your stored identity mapping to fetch the current content:
MARKET
POST /v1/sdk/clarity/market-update
x-api-key; body { "language": "en" }
ASSET
GET /v1/sdk/clarity/assets/{assetId}
x-api-key; use subjectId as assetId, with language query
PORTFOLIO
GET /v1/sdk/clarity/portfolios/{portfolioId}
x-api-key; use portfolioId, with language and optional window query
Request and response schemas: Swagger UI for sandbox or production.
MARKET
motif.clarity.marketUpdate({ language })
Organization API key from client setup
ASSET
motif.clarity.assetById(subjectId, {language})
Exact asset subject ID
PORTFOLIO
motif.clarity.portfolioById(portfolioId, {language, window: '1D'})
Exact native account ID; no user header
Use event.data.language and the stored mapping, not a guessed user or ticker. See the complete retrieval examples.
Use exact subject IDs for all asset categories, including custom assets. A foreign or archived subject returns 404; an authorized subject without an authored insight returns null or nullable content members. See the complete integration.
For a portfolio read, use the notification’s portfolioId directly with the exact-ID GET endpoint. Anonymous client accounts and several portfolios per client are supported. A chat partner-context ID is a different identifier.
These endpoints return the latest stored content, not a revision-specific copy of the notification. Another publication may have occurred before you read. Handle unavailable content (null or nullable fields) and stale status without labeling it a fresh insight. See Integrate Clarity and the API reference for the response shapes and source-document retrieval.
Retries, ordering and operations
Motif treats 2xx as success. Network failures, HTTP 408, 429 and 5xx are retried with exponential backoff, capped at one hour between attempts, up to eight attempts. Other unsuccessful HTTP statuses are terminal for that delivery. Redirects are not followed. Heavy processing should happen after acknowledgment so it does not cause timeouts and duplicate deliveries.
Delivery can be repeated and is not an ordered audit log. Newer assessment work can supersede a queued notification; Motif skips superseded deliveries. A disabled or deleted subscription, or loss of portfolio access, can also prevent a pending delivery. Use the latest-content read to reconcile after an outage rather than expecting every intermediate edition to arrive.
The delivery list shows HTTP status, success, duration, error and time. The admin delivery detail also exposes the sent payload; receiver response bodies are not retained. After a terminal failure, inspect the cause, correct the receiver and reconcile the current insights. Use the replay endpoint below for assessment attempts and reconcile current content with the exact subject ID.
The event picker also includes user, profile, strategy, report, KYC and order event names. Their presence does not establish automatic delivery for those workflows. The insight integration described here uses assessment.published for all three surfaces.
Replay an assessment notification
Use POST /v1/sdk/webhooks/{id}/deliveries/{deliveryId}/replay or motif.webhooks.replay(id, deliveryId), selecting the attempt ID from delivery history. The response is {eventId,status:"QUEUED"}. The original event ID is retained and current subscription, access and supersession checks still apply. Test events cannot be replayed. Reconcile current content when an older notification has been superseded.
Last updated