TrustLayer Webhooks
Webhooks let you receive real-time notifications from the TrustLayer platform whenever specific events occur. Configure endpoints, subscribe to events, and react to changes in your data as they happen.
| Contact | TrustLayer Support support@trustlayer.io |
|---|---|
| License | Apache 2.0 https://apache.org/licenses/LICENSE-2.0 |
| Terms of Service | https://trustlayer.io/terms-of-service |
| OpenAPI specification | /webhooks/webhooks-api.yaml |
Webhooks Overview
The Current webhooks model is the supported shape for all new integrations. It uses a generic object / record terminology that extends safely beyond Parties and Projects to any custom entity you define in TrustLayer.
Notifications are delivered via HTTP POST to a URL you register, carrying a JSON payload that describes the event and the records it applies to. The endpoint management API is shared with the legacy model — see Managing webhooks for how to register, list, and delete webhooks.
Request headers
Each delivery is an HTTP POST with the following headers:
| Header | Value |
|---|---|
Content-Type |
Always application/json. Cannot be overridden. |
User-Agent |
Defaults to TrustLayer-Webhooks/<version>. The version reflects the platform release that delivered the event and may change over time — do not match against a specific value. Can be overridden via workspace-configured custom headers. |
X-Hmac-Hash |
Sent only when an HMAC secret is configured for your workspace. Contains the HMAC-SHA256 of the JSON body, hex-encoded. Use it to verify request authenticity. Cannot be overridden. |
Workspace administrators can configure additional custom headers (for example, Authorization: Bearer …) that are appended to every delivery. Custom headers may override User-Agent but cannot replace Content-Type or X-Hmac-Hash.
If your endpoint sits behind a WAF or bot-protection service, make sure it does not block requests based on the
User-Agentvalue — legitimate webhook deliveries always carry one, and rules that filter unknown agents will reject them.
Terminology
TrustLayer models your data as a small set of generic entities. Each entity comes in two layers: the object (the schema) and the record (an individual instance).
| Concept | Description | Example object |
|---|---|---|
| Primary record | The subject of most events (e.g. a vendor you’re tracking). | Party |
| Context record | Scopes or groups primary records for a given use case. | Project |
| Request record | Connects a primary record with an optional context record, and tracks compliance for that pair. | Party on Project link |
Every event references at least one object / record pair, so consumers always know both what kind of thing changed and which instance it was.
Common payload shape
All current events include the following top-level fields:
{
"workspaceId": "ID of your TrustLayer workspace",
"eventType": "The event type identifier (see lists below)"
}
Legacy events use
organizationIdinstead ofworkspaceId. The underlying value is the same — the field name changed to match the rest of the platform.
Events that reference a primary entity add:
{
"primaryObject": { "id": "...", "name": "Party" },
"primaryRecord": { "id": "...", "name": "Acme Construction" }
}
Events that reference a context entity add:
{
"contextObject": { "id": "...", "name": "Project" },
"contextRecord": { "id": "...", "name": "North Campus" }
}
Events that reference a request entity always carry the primary pair, and optionally carry the context pair:
{
"requestObject": { "id": "...", "name": "Project-Party" },
"requestRecord": { "id": "...", "name": "Acme @ North Campus" },
"primaryObject": { "id": "...", "name": "Party" },
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"contextObject": { "id": "...", "name": "Project" },
"contextRecord": { "id": "...", "name": "North Campus" }
}
The .name field on a record is optional — it may be absent when the record has no display name yet (for example, a primary record created as a stub before enrichment).
Primary record events
Fired when a primary record is created, updated, or transitions state.
| Identifier | Description |
|---|---|
primary_record:created |
A new primary record was created |
primary_record:updated |
An existing primary record changed |
primary_record:deleted |
A primary record was deleted |
primary_record:activated |
A primary record was reactivated (unarchived) |
primary_record:archived |
A primary record was archived |
Payload:
{
"workspaceId": "...",
"eventType": "primary_record:created",
"primaryObject": { "id": "...", "name": "Party" },
"primaryRecord": { "id": "...", "name": "Acme Construction" }
}
Primary record tag events
Fired when a tag is attached to or removed from a primary record.
| Identifier | Description |
|---|---|
primary_record:tag:added |
A tag was added to a primary record |
primary_record:tag:removed |
A tag was removed from a primary record |
Payload: primary record payload plus a tag object.
{
"workspaceId": "...",
"eventType": "primary_record:tag:added",
"primaryObject": { "id": "...", "name": "Party" },
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"tag": { "id": "...", "name": "high-risk" }
}
Primary record attribute events
Fired when a custom-field value on a primary record is set, changed, or cleared. Each individual attribute change produces one delivery — bulk edits produce one event per changed attribute.
| Identifier | Description |
|---|---|
primary_record:attribute:updated |
A custom field on a primary record changed |
Payload: primary record payload plus an attribute delta with the new and previous value.
{
"workspaceId": "...",
"eventType": "primary_record:attribute:updated",
"primaryObject": { "id": "...", "name": "Party" },
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"attribute": {
"id": "...",
"name": "Region",
"value": { "value": "EMEA" },
"previousValue": { "value": "NA" }
}
}
| Field | Description |
|---|---|
attribute.id |
ID of the custom field whose value changed. |
attribute.name |
Display name of the custom field. |
attribute.value |
The new value. Omitted when the field is being cleared. |
attribute.previousValue |
The prior value. Omitted when the field had no value before. |
value.value / previousValue.value |
The literal value (string or number). For dropdown / option-typed fields, the option’s display value. |
value.optionId / previousValue.optionId |
Set for dropdown / option-typed fields. The id of the chosen option. |
Primary record message events
Fired for conversation-level email activity tied to a primary record. Payloads are metadata only — message bodies and attachments are never delivered through webhooks.
| Identifier | Description |
|---|---|
primary_record:message:sent |
A message was sent to a primary record |
primary_record:message:received |
A message was received from a primary record’s contact |
primary_record:message:failed |
Delivery of a message failed (bounce, invalid address, etc.) |
The payload shape varies by sub-type.
primary_record:message:sent
{
"workspaceId": "...",
"eventType": "primary_record:message:sent",
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"conversationId": "...",
"conversationSubject": "Certificate of insurance renewal",
"senderEmail": "ops@yourcompany.example",
"senderName": "Jane Operator",
"recipients": ["insurance@acme.example"]
}
primary_record:message:received
{
"workspaceId": "...",
"eventType": "primary_record:message:received",
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"conversationId": "...",
"conversationSubject": "Re: Certificate of insurance renewal",
"senderEmail": "insurance@acme.example",
"senderName": "Acme Insurance"
}
primary_record:message:failed
{
"workspaceId": "...",
"eventType": "primary_record:message:failed",
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"contactEmail": "insurance@acme.example",
"reason": "Mailbox does not exist"
}
reason is best-effort and may be omitted if the upstream provider did not report one.
Context record events
Fired when a context record (e.g. a Project) is created, updated, or transitions state.
| Identifier | Description |
|---|---|
context_record:created |
A new context record was created |
context_record:updated |
A context record changed |
context_record:deleted |
A context record was deleted |
context_record:activated |
A context record was activated |
context_record:archived |
A context record was archived |
Payload:
{
"workspaceId": "...",
"eventType": "context_record:created",
"contextObject": { "id": "...", "name": "Project" },
"contextRecord": { "id": "...", "name": "North Campus" }
}
Context record attribute events
Fired when a custom-field value on a context record is set, changed, or cleared. Each individual attribute change produces one delivery. Date-typed values are normalized to ISO 8601 strings on the wire.
| Identifier | Description |
|---|---|
context_record:attribute:updated |
A custom field on a context record changed |
Payload: context record payload plus an attribute delta with the new and previous value.
{
"workspaceId": "...",
"eventType": "context_record:attribute:updated",
"contextObject": { "id": "...", "name": "Project" },
"contextRecord": { "id": "...", "name": "North Campus" },
"attribute": {
"id": "...",
"name": "StartDate",
"value": { "value": "2026-01-01T00:00:00.000Z" },
"previousValue": { "value": "2025-12-01T00:00:00.000Z" }
}
}
The attribute object follows the same shape as in Primary record attribute events. Date-typed custom fields are delivered as ISO 8601 strings.
Request record events
Fired when the link between a primary record and an (optional) context record is created, updated, or transitions state. A request record without a context represents a global requirement on a primary record.
| Identifier | Description |
|---|---|
request_record:created |
A request record was created (e.g. a party was added to a project) |
request_record:updated |
A request record changed |
request_record:deleted |
A request record was deleted (e.g. a party was removed from a project) |
request_record:activated |
A request record was activated |
request_record:archived |
A request record was archived |
Payload:
{
"workspaceId": "...",
"eventType": "request_record:created",
"requestObject": { "id": "...", "name": "Project-Party" },
"requestRecord": { "id": "...", "name": "Acme @ North Campus" },
"primaryObject": { "id": "...", "name": "Party" },
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"contextObject": { "id": "...", "name": "Project" },
"contextRecord": { "id": "...", "name": "North Campus" }
}
contextObject and contextRecord are omitted when the request record is not scoped to a context.
Request record attribute events
Fired when a custom-field value on a request record is set, changed, or cleared. Each individual attribute change produces one delivery — bulk edits produce one event per changed attribute.
| Identifier | Description |
|---|---|
request_record:attribute:updated |
A custom field on a request record changed |
Payload: request record payload plus an attribute delta with the new and previous value.
{
"workspaceId": "...",
"eventType": "request_record:attribute:updated",
"requestObject": { "id": "...", "name": "Project-Party" },
"requestRecord": { "id": "...", "name": "Acme @ North Campus" },
"primaryObject": { "id": "...", "name": "Party" },
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"contextObject": { "id": "...", "name": "Project" },
"contextRecord": { "id": "...", "name": "North Campus" },
"attribute": {
"id": "...",
"name": "Status",
"value": { "optionId": "...", "value": "Approved" },
"previousValue": { "optionId": "...", "value": "Pending" }
}
}
contextObject and contextRecord are omitted when the request record is not scoped to a context. The attribute object follows the same shape as in Primary record attribute events.
Primary record document events
Fired when a document is associated with a primary record. This is the v2 event for the document-to-primary-record relationship; it dispatches alongside the legacy document:assigned:to:party.
Metadata-only. Webhook payloads never carry document file contents, binary data, or storage keys. Fetch the document via the REST API using
document.id.
| Identifier | Description |
|---|---|
document:primary_record:assigned |
A document was assigned to a primary record |
Payload — v2 common envelope plus primaryRecord, document, and the acting user’s identity. Unlike other primary-record events, primaryObject is not included.
{
"workspaceId": "...",
"eventType": "document:primary_record:assigned",
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"document": { "id": "...", "name": "policy.pdf" },
"userName": "Jane Operator",
"userEmail": "jane@yourcompany.example"
}
| Field | Description |
|---|---|
primaryRecord.id |
ID of the primary record (for example, the Party) the document was assigned to. |
primaryRecord.name |
Display name of the primary record. Always present and non-empty — falls back to "Unknown" when the source has no name. |
document.id |
ID of the assigned document. |
document.name |
File name of the assigned document. May be an empty string. |
userName |
Name of the user who performed the assignment. Empty string for system / automated actors. |
userEmail |
Email of the user who performed the assignment. Empty string for system / automated actors. |
Triggers. Fires whenever a document is attached to a primary record — through the UI, a document upload that resolves to a known primary record, or the REST API. Bulk assignments produce one webhook delivery per (document, primaryRecord) pair, not a single batched payload. Reassigning a document to a different primary record fires a new event for the new target.
Parallel legacy dispatch. Every successful document-to-primary-record assignment also fires the legacy document:assigned:to:party event. Legacy dispatch is best-effort: it is wrapped in a guard so that a legacy delivery failure cannot block the v2 event or the underlying assignment. Both events follow at-least-once delivery semantics, the same as every other webhook.
v1 → v2 field mapping
For integrations migrating from the legacy document:assigned:to:party event:
Legacy (document:assigned:to:party) |
Current (document:primary_record:assigned) |
|---|---|
organizationId |
workspaceId |
partyId |
primaryRecord.id |
partyName |
primaryRecord.name (always present; falls back to "Unknown") |
documentId |
document.id |
documentName |
document.name |
userName |
userName |
userEmail |
userEmail |
Request record compliance events
Fired when the compliance status of a request record changes. The score is always an integer from 0 to 100.
| Identifier | Description |
|---|---|
request_record_compliance:changed |
The compliance score of the request record changed |
request_record_compliance:reached |
The request record reached full compliance |
request_record_compliance:lost |
The request record fell out of compliance |
Payload: the request record payload plus compliance fields.
{
"workspaceId": "...",
"eventType": "request_record_compliance:reached",
"requestObject": { "id": "...", "name": "Project-Party" },
"requestRecord": { "id": "...", "name": "Acme @ North Campus" },
"primaryObject": { "id": "...", "name": "Party" },
"primaryRecord": { "id": "...", "name": "Acme Construction" },
"contextObject": { "id": "...", "name": "Project" },
"contextRecord": { "id": "...", "name": "North Campus" },
"prevSubjectsComplianceScore": 80,
"subjectsComplianceScore": 100,
"subjectsCount": 5,
"compliantSubjectsCount": 5
}
| Field | Description |
|---|---|
prevSubjectsComplianceScore |
Score immediately before this event. Omitted on first evaluation. |
subjectsComplianceScore |
Current compliance score (0–100). |
subjectsCount |
Total number of subjects evaluated on the request record. |
compliantSubjectsCount |
Number of those subjects currently compliant. |
Full event list
| Identifier | Category |
|---|---|
primary_record:created |
Primary record |
primary_record:updated |
Primary record |
primary_record:deleted |
Primary record |
primary_record:activated |
Primary record |
primary_record:archived |
Primary record |
primary_record:tag:added |
Primary record — tag |
primary_record:tag:removed |
Primary record — tag |
primary_record:attribute:updated |
Primary record — attribute |
primary_record:message:sent |
Primary record — message |
primary_record:message:received |
Primary record — message |
primary_record:message:failed |
Primary record — message |
document:primary_record:assigned |
Primary record — document |
context_record:created |
Context record |
context_record:updated |
Context record |
context_record:deleted |
Context record |
context_record:activated |
Context record |
context_record:archived |
Context record |
context_record:attribute:updated |
Context record — attribute |
request_record:created |
Request record |
request_record:updated |
Request record |
request_record:deleted |
Request record |
request_record:activated |
Request record |
request_record:archived |
Request record |
request_record:attribute:updated |
Request record — attribute |
request_record_compliance:changed |
Compliance |
request_record_compliance:reached |
Compliance |
request_record_compliance:lost |
Compliance |
Wildcards are supported when subscribing — e.g. primary_record:* matches every primary record event, including tag and message events.
Webhooks Overview
Webhooks allow third-party developers to be notified whenever a specific event occurs on the TrustLayer platform. Notifications are performed via HTTP POST calls to a provided endpoint URL, carrying with them a payload with the relevant data.
You can set up webhooks either via the UI (in the Settings→TrustLayer API pane), or via API calls (with more precise control).
Request headers
Each delivery is an HTTP POST with the following headers:
| Header | Value |
|---|---|
Content-Type |
Always application/json. Cannot be overridden. |
User-Agent |
Defaults to TrustLayer-Webhooks/<version>. The version reflects the platform release that delivered the event and may change over time — do not match against a specific value. Can be overridden via workspace-configured custom headers. |
X-Hmac-Hash |
Sent only when an HMAC secret is configured for your workspace. Contains the HMAC-SHA256 of the JSON body, hex-encoded. Use it to verify request authenticity. Cannot be overridden. |
Workspace administrators can configure additional custom headers (for example, Authorization: Bearer …) that are appended to every delivery. Custom headers may override User-Agent but cannot replace Content-Type or X-Hmac-Hash.
If your endpoint sits behind a WAF or bot-protection service, make sure it does not block requests based on the
User-Agentvalue — legitimate webhook deliveries always carry one, and rules that filter unknown agents will reject them.
The payload that is sent with each webhook notification will have some common attributes:
{
"organizationId": "The id of your organization",
"eventType": "The type identifier of the event (see list below)"
}
Most webhooks are relative either to a party or a project (or both). In this case the payload will carry these properties:
{
"partyId": "Id of the affected party",
"partyName": "Name of the party",
"projectId": "Id of the affected project",
"projectName": "Name of the project"
}
For tag-related events, these attributes are included:
{
"tagId": "Id of the affected tag",
"tagName": "Name of the tag"
}
For document-related events, these attributes are supported (not all of them will be present at all times):
{
"documentId": "Id of the affected document",
"documentName": "Name of the document",
"documentReviewed": "Boolean flag, true if the document was reviewed",
"flagSeverityLevel": "Either 'low', 'medium' or 'high'",
"flagNotes": "Text entered in the flag notes",
"verificationNotes": "Text entered in the verification notes"
}
For document:assigned:to:party, the payload includes the acting user’s identity:
{
"userName": "Name of the user who performed the assignment",
"userEmail": "Email of the user who performed the assignment"
}
Both fields are empty strings for system or automated actors. Document file contents are never delivered through webhooks — fetch the document through the REST API using documentId.
Compliance-related events always carry this attribute:
{
"complianceScore": "Numeric value representing the score of the compliance status (0-100)"
}
Currently, these events are supported:
| Identifier | Description |
|---|---|
| comment:created | A new comment was added to the activity timeline |
| party:created | A new party was created |
| party:updated | A change was made to an existing party |
| party:deleted | A party was deleted |
| party:tag:added | A tag was added to a party |
| party:tag:removed | A tag was removed from a party |
| project:created | A new project was created |
| project:updated | A change was made to an existing project |
| project:deleted | A project was deleted |
| project_compliance:lost | Project-level compliance is no longer satisfied |
| project:activated | A project was activated |
| project:inactivated | A project was inactivated |
| project:party_added | A party was added to a project |
| project:party_removed | A party was removed from a project |
| party_compliance:changed | The compliance score of a party changed |
| party_compliance:reached | A party’s global compliance is satisfied |
| party_compliance:lost | A party’s global compliance is no longer satisfied |
| project_compliance:changed | A party’s project-level compliance score changed |
| project_compliance:reached | A party’s project-level compliance is satisfied |
| document:uploaded | A document was uploaded |
| document:processed | A document was processed |
| document:reviewed | A document was reviewed |
| document:archived | A document was archived |
| document:flagged | A flag was added to a document |
| document:verified | A document was verified |
| document:flag_removed | A flag was removed from a document |
| document:verification_removed | A verification was removed from a document |
| document:deleted | A document was deleted |
| document:assigned:to:party | A document was assigned to a party |
Managing webhooks
You can create a webhook via the public API:
curl -XPOST -H"Authorization: Bearer ${token}" \
--data '{ \
"id": "webhook_id", \
"namespace": "my_app", \
"url": "https://my-app.example.com/notify", \
"events": ["party:created", "party:updated"], \
"enabled": true
}' \
https://api.trustlayer.io/v1/webhooks
- The
namespaceparameter can be whatever you want, but the"trustlayer"value is reserved for internal use. - The
urlparameter needs to be unique relative to thenamespace; if you perform a second POST request using the same values, you will overwrite the existing webhook - The
eventsparameter is a list of the events you’re interested in; they can also be specified using wildcards (e.g."party:*") to express interest for a whole class of events. Finally, if you specify an empty list, your endpoint will be notified for all events. - The
enabledparameter can be omitted, and it will default totrue.
Note that webhooks created via the API will not be displayed in the platform Settings page. You can list these webhooks via a GET request:
curl -XGET -H "Authorization: Bearer ${token}" https://api.trustlayer.io/v1/webhooks
You can also delete a webhook via the API:
curl -XDELETE
-H "Authorization: Bearer ${token}" \
https://api.trustlayer.io/v1/webhooks/webhook_id
Where webhook_id is the unique identifier of the webhook you want to delete as defined in the POST response:
{
"id": "webhook_id",
"namespace": "my_app",
"url": "https://my-app.example.com/notify",
"events": ["party:created", "party:updated"],
"enabled": true
}
TrustLayer Webhooks — Legacy management API
Legacy REST endpoints for managing webhook subscriptions and organization-level webhook settings. Retained for backwards compatibility. All requests are made against https://api.trustlayer.io/v1 with a valid API token in the Authorization: Bearer header.
Webhooks
GET /webhooks
List webhooks
List webhook subscriptions.
Available filters:
namespace
https://api.trustlayer.io/v1/webhooks?filter[namespace]=value
Query Parameters
| filter[namespace] | optional |
Filter webhooks by namespace. |
|
Responses
200 OKContent-Type: application/json{"data":[{"id":"string","type":"string","namespace":"string","url":"string","events":["string"],"enabled":"boolean","createdAt":"string"}
],"status":"string","meta":{"count":"number","pages":"number","totalCount":"number","totalPages":"number"}
}
POST /webhooks
Create a webhook
Create a webhook subscription.
namespacemay be any value you choose, but the valuetrustlayeris reserved for internal use.urlmust be unique within a namespace. Re-posting with the samenamespace+urloverwrites the existing webhook.eventsaccepts event identifiers or wildcards (for exampleparty:*). An empty list subscribes to all events.enableddefaults totrue.
https://api.trustlayer.io/v1/webhooks
Request Body
Content-Type: application/json{"webhook":{"namespace":"string","url":"string","events":["string"],"enabled":"boolean"}
}
Responses
200 OKContent-Type: application/json{"status":"string","data":{"id":"string","type":"string","namespace":"string","url":"string","events":["string"],"enabled":"boolean","createdAt":"string"}
}
Settings
GET /settings/webhooks
Fetch webhooks settings
Returns the organization-level settings for webhooks.
https://api.trustlayer.io/v1/settings/webhooks
Responses
200 OKContent-Type: application/json{"status":"string","data":{"headers":[{"name":"string","value":"string"}
]}
}
PUT /settings/webhooks
Update webhooks settings
Updates the organization-level settings for webhooks. Custom headers configured here are appended to every webhook delivery.
https://api.trustlayer.io/v1/settings/webhooks
Request Body
Content-Type: application/json{"settings":{"headers":[{"name":"string","value":"string"}
]}
}
Responses
200 OKContent-Type: application/json{"settings":{"headers":[{"name":"string","value":"string"}
]}
}
Models
webhook
id |
string |
type |
string |
namespace |
string |
url |
string |
events |
array |
enabled |
boolean |
createdAt |
string |
Webhook settings
headers |
array |