API
Webhooks
Subscribe to change events on ledger and ACH entities and receive them as signed HTTP requests.
Webhooks deliver change events for records in your ledger to an HTTPS endpoint you control. Whenever a subscribed record is inserted, updated, or deleted, Twisp sends an HTTP POST to your endpoint with the new state of the record (and the previous state, when one exists).
Creating an Endpoint
Subscribe to events by creating an endpoint with the createEndpoint mutation:
mutation Setup {
events {
createEndpoint(
input: {
endpointId: "345940ed-2726-4b20-88aa-820857ac0e68"
status: ENABLED
endpointType: WEBHOOK
url: "https://yourdomain.com/path/to/hooks"
description: "subscribe to balance events"
subscription: ["balance.*"]
filters: {
isMyCalcId: "document.calculation_id == uuid('5542ce2c-e43a-40b1-b04b-5dfb06f9f4d8')"
}
}
) {
endpointId
signingSecret
}
}
}
- Replace
urlwith your actual webhook URL. - Customize
subscriptionto include the event types you are interested in. (subscription: ["*"]andfilters: {}will get you the firehose.) - Save the returned
signingSecret— you will need it to verify request signatures.
See EndpointInput for all configuration options.
Webhooks work on the local version of Twisp too.
Subscriptions
Each subscription is a string in the format <entity>.<action>, where action is one of inserted, updated, or deleted. Wildcards (*) are supported, so balance.* matches every balance event and * matches everything.
Supported entities:
journalaccountaccountcontextaccountsetaccountsetmembertrancodetransactionentrybalancecustomindexcustombalanceendpointkvvalue— transactional key/value records
Supported ACH entities:
configuration— ACH processing configurationsfileinfo— processing status of an ACH filefilerecord— individual records within an ACH fileworkflowtrace— traces of ACH workflow execution
For example, to track the processing status of ACH files, subscribe to fileinfo.inserted and fileinfo.updated.
Filters
The optional filters field is a map of named CEL expressions evaluated against each candidate event. The record is available as document, with fields in snake_case. An event is sent only if all expressions evaluate to true (they are combined with a logical AND).
For example, to receive transaction.inserted events only for specific tran codes:
mutation CreateEvents($endpointId: UUID = "uuid.New()" @cel) {
events {
createEndpoint(
input: {
endpointId: $endpointId
endpointType: WEBHOOK
url: "https://yourdomain.com/path/to/hooks"
subscription: ["transaction.inserted"]
description: "Subscribe to ACH_SETTLE_CR and ACH_SETTLE_DR"
filters: {
isWorkflowSettle: "document.tran_code_id in [uuid('d918eb34-1ef9-4437-a82b-46c169f63e41'), uuid('5af51352-84ad-4534-aacf-4e68c2ed2e2b')]"
}
}
) {
endpointId
signingSecret
}
}
}
Event Payload
Events are delivered as a JSON document with the following envelope:
| Field | Type | Description |
|---|---|---|
eventId | UUID | Unique identifier for this event. |
eventType | String | The matched subscription, e.g. balance.updated. |
accountId | String | The Twisp tenant account id this event originated from. |
region | String | Region identifier this event originated from. |
created | Timestamp | When the event was created. |
recordRowId | UUID | Unique identifier of the record in data. |
recordVersion | Integer | Monotonically increasing version of the record. |
data | Object | The state of the record after the change. |
previous | Object | The state of the record before the change, when one exists. |
Payloads use camelCase JSON keys. Timestamps, enumerations, dates, and UUIDs are represented as strings. Money values use an object with string units and currency fields.
Payload Examples
The examples below are generated from the linked protobuf messages and serialized with the same webhook formatter used by Twisp. They show inserted events, where previous is null; updated events include the prior record in previous. Twisp adds version to each serialized record.
Journal
Subscription: journal.*. Schema: twisp.core.v1.Journal.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "journal.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"code": "EXAMPLE",
"config": {
"enableEffectiveBalances": true
},
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z",
"name": "Example",
"status": "ACTIVE",
"version": 1
},
"previous": null
}
Account
Subscription: account.*. Schema: twisp.core.v1.Account.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "account.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"accountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"code": "EXAMPLE",
"config": {
"enableConcurrentPosting": true,
"idempotent": false,
"isAccountSet": false,
"upsert": false
},
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"externalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"metadata": {
"key": "value"
},
"modified": "2026-08-26T18:00:00Z",
"name": "Example",
"normalBalanceType": "CREDIT",
"status": "ACTIVE",
"version": 1
},
"previous": null
}
Account context
Subscription: accountcontext.*. Schema: twisp.core.v1.AccountContext.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "accountcontext.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"accountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"created": "2026-08-26T18:00:00Z",
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z",
"parentAccounts": [
{
"accountId": "345940ed-2726-4b20-88aa-820857ac0e68"
}
],
"version": 1
},
"previous": null
}
Account set
Subscription: accountset.*. Schema: twisp.core.v1.AccountSet.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "accountset.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"accountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"accountSetId": "345940ed-2726-4b20-88aa-820857ac0e68",
"code": "EXAMPLE",
"config": {
"enableConcurrentPosting": true,
"idempotent": false,
"upsert": false
},
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"hasMembers": true,
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"metadata": {
"key": "value"
},
"modified": "2026-08-26T18:00:00Z",
"name": "Example",
"status": "ACTIVE",
"version": 1
},
"previous": null
}
Account set member
Subscription: accountsetmember.*. Schema: twisp.core.v1.AccountSetMember.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "accountsetmember.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"accountSetId": "345940ed-2726-4b20-88aa-820857ac0e68",
"created": "2026-08-26T18:00:00Z",
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"memberId": "345940ed-2726-4b20-88aa-820857ac0e68",
"memberType": "ACCOUNT",
"version": 1
},
"previous": null
}
Tran code
Subscription: trancode.*. Schema: twisp.core.v1.TranCode.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "trancode.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"assertions": {
"key": "value"
},
"code": "EXAMPLE",
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"entries": [
{
"accountId": "000000000000",
"condition": "example",
"currency": "USD",
"description": "Example record",
"direction": "example",
"entryType": "EXAMPLE",
"layer": "example",
"metadata": "{'key': 'value'}",
"units": "example"
}
],
"metadata": {
"key": "value"
},
"modified": "2026-08-26T18:00:00Z",
"params": [
{
"default": "example",
"description": "Example record",
"example": "example",
"name": "Example",
"type": "INT"
}
],
"status": "ACTIVE",
"tranCodeId": "345940ed-2726-4b20-88aa-820857ac0e68",
"transaction": {
"correlationId": "345940ed-2726-4b20-88aa-820857ac0e68",
"description": "Example record",
"effective": "example",
"externalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"metadata": "{'key': 'value'}"
},
"vars": {
"key": "value"
},
"version": 1,
"workflow": {
"executionId": "345940ed-2726-4b20-88aa-820857ac0e68",
"params": {
"example": "example"
},
"task": "example",
"workflowId": "345940ed-2726-4b20-88aa-820857ac0e68"
}
},
"previous": null
}
Transaction
Subscription: transaction.*. Schema: twisp.core.v1.Transaction.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "transaction.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"correlationId": "345940ed-2726-4b20-88aa-820857ac0e68",
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"effective": "2026-08-26",
"externalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"metadata": {
"key": "value"
},
"modified": "2026-08-26T18:00:00Z",
"properties": {
"groups": [
"example"
],
"overrideVelocityEnforcement": {
"action": "VOID"
}
},
"tranCodeId": "345940ed-2726-4b20-88aa-820857ac0e68",
"tranCodeVersion": 1,
"transactionId": "345940ed-2726-4b20-88aa-820857ac0e68",
"version": 1
},
"previous": null
}
Entry
Subscription: entry.*. Schema: twisp.core.v1.Entry.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "entry.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"accountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"amount": {
"currency": "USD",
"units": "123.45"
},
"balanceRecordId": "345940ed-2726-4b20-88aa-820857ac0e68",
"balanceRecordVersion": 1,
"committed": "2026-08-26T18:00:00Z",
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"direction": "DEBIT",
"entryId": "345940ed-2726-4b20-88aa-820857ac0e68",
"entryType": "EXAMPLE",
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"layer": "SETTLED",
"metadata": {
"key": "value"
},
"modified": "2026-08-26T18:00:00Z",
"parentAccountIds": [
"345940ed-2726-4b20-88aa-820857ac0e68"
],
"transactionId": "345940ed-2726-4b20-88aa-820857ac0e68",
"transactionSeq": 1,
"version": 1
},
"previous": null
}
Balance
Subscription: balance.*. Schema: twisp.core.v1.Balance.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "balance.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"accountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"availableEncumbrance": {
"crBalance": {
"currency": "USD",
"units": "123.45"
},
"drBalance": {
"currency": "USD",
"units": "123.45"
},
"entryId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z"
},
"availablePending": {
"crBalance": {
"currency": "USD",
"units": "123.45"
},
"drBalance": {
"currency": "USD",
"units": "123.45"
},
"entryId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z"
},
"availableSettled": {
"crBalance": {
"currency": "USD",
"units": "123.45"
},
"drBalance": {
"currency": "USD",
"units": "123.45"
},
"entryId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z"
},
"calculationId": "345940ed-2726-4b20-88aa-820857ac0e68",
"created": "2026-08-26T18:00:00Z",
"currency": "USD",
"dimension": "ZXhhbXBsZQ==",
"dimensions": {
"key": "value"
},
"encumbrance": {
"crBalance": {
"currency": "USD",
"units": "123.45"
},
"drBalance": {
"currency": "USD",
"units": "123.45"
},
"entryId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z"
},
"entryCommitted": "2026-08-26T18:00:00Z",
"entryId": "345940ed-2726-4b20-88aa-820857ac0e68",
"entryTimestamps": [
1
],
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z",
"pending": {
"crBalance": {
"currency": "USD",
"units": "123.45"
},
"drBalance": {
"currency": "USD",
"units": "123.45"
},
"entryId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z"
},
"settled": {
"crBalance": {
"currency": "USD",
"units": "123.45"
},
"drBalance": {
"currency": "USD",
"units": "123.45"
},
"entryId": "345940ed-2726-4b20-88aa-820857ac0e68",
"modified": "2026-08-26T18:00:00Z"
},
"transactionId": "345940ed-2726-4b20-88aa-820857ac0e68",
"version": 1
},
"previous": null
}
Custom index
Subscription: customindex.*. Schema: twisp.db.v1.CustomIndex.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "customindex.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"id": "ZXhhbXBsZQ==",
"index": {
"filters": {
"example": "example"
},
"id": "ZXhhbXBsZQ==",
"partition": [
{
"alias": "example",
"order": "DESC",
"type": "INT",
"value": "example"
}
],
"properties": {
"asynchronous": false,
"buckets": 1,
"external": false,
"historical": false,
"nonCartesian": false,
"system": false,
"unique": false
},
"referencedBy": [
"example"
],
"schema": {
"key": "value"
},
"sort": [
{
"alias": "example",
"order": "DESC",
"type": "INT",
"value": "example"
}
],
"state": "DELETE_ONLY"
},
"name": "Example",
"on": "Account",
"synchronization": "SYNCHRONOUS",
"version": 1,
"viewName": "Example"
},
"previous": null
}
Custom balance
Subscription: custombalance.*. Schema: twisp.core.v1.Calculation.
Custom balance records use the Calculation protobuf message.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "custombalance.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"backfillStatus": "IN_PROGRESS",
"calculationId": "345940ed-2726-4b20-88aa-820857ac0e68",
"code": "EXAMPLE",
"condition": "example",
"config": {
"effectiveDateSource": "example",
"effectiveGranularity": "YEAR",
"enableEffectiveBalances": true,
"parentCalculationId": "345940ed-2726-4b20-88aa-820857ac0e68"
},
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"dimensions": [
{
"alias": "example",
"type": "INT",
"value": "example"
}
],
"modified": "2026-08-26T18:00:00Z",
"scope": "GLOBAL",
"skipCalculation": false,
"status": "ACTIVE",
"version": 1
},
"previous": null
}
Endpoint
Subscription: endpoint.*. Schema: twisp.core.v1.Endpoint.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "endpoint.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"accountId": "000000000000",
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"endpointId": "345940ed-2726-4b20-88aa-820857ac0e68",
"endpointType": "WEBHOOK",
"filters": {
"example": "example"
},
"modified": "2026-08-26T18:00:00Z",
"signingSecret": "whsec_example",
"status": "ENABLED",
"subscription": [
"balance.*"
],
"url": "https://yourdomain.com/path/to/hooks",
"version": 1
},
"previous": null
}
Key/value record
Subscription: kvvalue.*. Schema: twisp.core.v1.KVValue.
Key/value records use the KVValue protobuf message.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "kvvalue.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"conditions": {
"example": "example"
},
"created": "2026-08-26T18:00:00Z",
"description": "Example record",
"key": "customer-123",
"modified": "2026-08-26T18:00:00Z",
"namespace": "customers",
"value": {
"key": "value"
},
"version": 1
},
"previous": null
}
ACH configuration
Subscription: configuration.*. Schema: twisp.ach.v1.Configuration.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "configuration.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"autoPending": false,
"configurationId": "345940ed-2726-4b20-88aa-820857ac0e68",
"direction": "BOTH",
"endpointId": "345940ed-2726-4b20-88aa-820857ac0e68",
"exceptionAccountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"feeAccountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"fileModifierConfiguration": {
"endFileIdModifier": "example",
"startFileIdModifier": "example",
"userSupplied": false
},
"journalId": "345940ed-2726-4b20-88aa-820857ac0e68",
"lastFileDate": "2026-08-26T18:00:00Z",
"lastFileIdModifier": "example",
"lastTraceNumber": 1,
"odfiBatchHeader": {
"enableBalancedReturnNocs": true,
"includeOffset": false,
"offsetAccountNumber": "example",
"offsetAccountType": "EXAMPLE",
"offsetDescription": "Example record",
"offsetRoutingNumber": "example"
},
"odfiFileHeader": {
"immediateDestination": "example",
"immediateDestinationName": "Example",
"immediateOrigin": "example",
"immediateOriginName": "Example"
},
"pendingAccountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"settlementAccountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"suspenseAccountId": "345940ed-2726-4b20-88aa-820857ac0e68",
"timeZone": "example",
"traceNumberConfiguration": {
"maxTraceNumber": 1,
"minTraceNumber": 1
},
"usedFileIdModifiers": 1,
"version": 1
},
"previous": null
}
ACH file info
Subscription: fileinfo.*. Schema: twisp.ach.v1.FileInfo.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "fileinfo.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"configId": "345940ed-2726-4b20-88aa-820857ac0e68",
"configVersion": 1,
"created": "2026-08-26T18:00:00Z",
"fileDateTime": "example",
"fileId": "345940ed-2726-4b20-88aa-820857ac0e68",
"fileKey": "example",
"fileModifier": "example",
"fileVersion": "example",
"hasExceptions": true,
"metadata": {
"key": "value"
},
"modified": "2026-08-26T18:00:00Z",
"processingDetail": "example",
"processingStatistics": {
"numEntriesUnprocessed": 1,
"totalCreditAmount": "example",
"totalDebitAmount": "example"
},
"processingStatus": "VALIDATING",
"processingType": "RDFI",
"version": 1
},
"previous": null
}
ACH file record
Subscription: filerecord.*. Schema: twisp.ach.v1.FileRecord.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "filerecord.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"Value": {
"file": {
"advControl": {
"batchCount": 1,
"blockCount": 1,
"entryAddendaCount": 1,
"entryHash": 1,
"lineNumber": 1,
"totalCreditEntryDollarAmountInFile": 1,
"totalDebitEntryDollarAmountInFile": 1
},
"batches": [
{
"advControl": {
"achOperatorData": "example",
"batchNumber": 1,
"entryAddendaCount": 1,
"entryHash": 1,
"lineNumber": 1,
"odfiIdentification": "example",
"serviceClassCode": 1,
"totalCreditEntryDollarAmount": 1,
"totalDebitEntryDollarAmount": 1
},
"advEntries": [
{
"achOperatorData": "example",
"achOperatorRoutingNumber": "example",
"addendaRecordIndicator": 1,
"adviceRoutingNumber": "example",
"amount": 1,
"category": "example",
"checkDigit": "example",
"dfiAccountNumber": "example",
"discretionaryData": "example",
"fileIdentification": "example",
"individualName": "Example",
"julianDay": 1,
"lineNumber": 1,
"rdfiIdentification": "example",
"sequenceNumber": 1,
"transactionCode": 1
}
],
"category": "example",
"control": {
"batchNumber": 1,
"companyIdentification": "example",
"entryAddendaCount": 1,
"entryHash": 1,
"lineNumber": 1,
"messageAuthenticationCode": "EXAMPLE",
"odfiIdentification": "example",
"serviceClassCode": 1,
"totalCreditEntryDollarAmount": 1,
"totalDebitEntryDollarAmount": 1
},
"entries": [
{
"addendaRecordIndicator": 1,
"amount": 1,
"category": "example",
"checkDigit": "example",
"dfiAccountNumber": "example",
"discretionaryData": "example",
"identificationNumber": "example",
"individualName": "Example",
"lineNumber": 1,
"rdfiIdentification": "example",
"traceNumber": "example",
"transactionCode": 1
}
],
"header": {
"batchNumber": 1,
"companyDescriptiveDate": "example",
"companyDiscretionaryData": "example",
"companyEntryDescription": "Example record",
"companyIdentification": "example",
"companyName": "Example",
"effectiveEntryDate": "example",
"lineNumber": 1,
"odfiIdentification": "example",
"originatorStatusCode": 1,
"serviceClassCode": 1,
"settlementDate": "example",
"standardEntryClassCode": "EXAMPLE"
},
"offset": {
"accountNumber": "example",
"accountType": "EXAMPLE",
"description": "Example record",
"routingNumber": "example"
}
}
],
"control": {
"batchCount": 1,
"blockCount": 1,
"entryAddendaCount": 1,
"entryHash": 1,
"lineNumber": 1,
"totalCreditEntryDollarAmountInFile": 1,
"totalDebitEntryDollarAmountInFile": 1
},
"header": {
"blockingFactor": "example",
"fileCreationDate": "example",
"fileCreationTime": "example",
"fileIdModifier": "example",
"formatCode": "EXAMPLE",
"immediateDestination": "example",
"immediateDestinationName": "Example",
"immediateOrigin": "example",
"immediateOriginName": "Example",
"lineNumber": 1,
"priorityCode": "EXAMPLE",
"recordSize": "example",
"referenceCode": "EXAMPLE"
},
"iatBatches": [
{
"category": "example",
"control": {
"batchNumber": 1,
"companyIdentification": "example",
"entryAddendaCount": 1,
"entryHash": 1,
"lineNumber": 1,
"messageAuthenticationCode": "EXAMPLE",
"odfiIdentification": "example",
"serviceClassCode": 1,
"totalCreditEntryDollarAmount": 1,
"totalDebitEntryDollarAmount": 1
},
"entries": [
{
"addendaRecordIndicator": 1,
"addendaRecords": 1,
"amount": 1,
"category": "example",
"checkDigit": "example",
"dfiAccountNumber": "example",
"lineNumber": 1,
"ofacScreeningIndicator": "example",
"rdfiIdentification": "example",
"secondaryOfacScreeningIndicator": "example",
"traceNumber": "example",
"transactionCode": 1
}
],
"header": {
"batchNumber": 1,
"companyEntryDescription": "Example record",
"effectiveEntryDate": "example",
"foreignExchangeIndicator": "example",
"foreignExchangeReference": "example",
"foreignExchangeReferenceIndicator": 1,
"iatIndicator": "example",
"isoDestinationCountryCode": "EXAMPLE",
"isoDestinationCurrencyCode": "USD",
"isoOriginatingCurrencyCode": "USD",
"lineNumber": 1,
"odfiIdentification": "example",
"originatorIdentification": "example",
"originatorStatusCode": 1,
"serviceClassCode": 1,
"settlementDate": "example",
"standardEntryClassCode": "EXAMPLE"
}
}
]
}
},
"batchCount": 1,
"batchPosition": 1,
"created": "2026-08-26T18:00:00Z",
"entryCount": 1,
"entryPosition": 1,
"fileId": "345940ed-2726-4b20-88aa-820857ac0e68",
"fileKey": "example",
"recordCount": 1,
"recordId": "345940ed-2726-4b20-88aa-820857ac0e68",
"recordPosition": 1,
"recordType": "FILE",
"version": 1
},
"previous": null
}
ACH workflow trace
Subscription: workflowtrace.*. Schema: twisp.ach.v1.WorkflowTrace.
{
"eventId": "3f6e9283-5d96-546e-87db-3bf3c7d29011",
"eventType": "workflowtrace.inserted",
"accountId": "000000000000",
"region": "us-west-2",
"created": "2026-08-26T18:00:00Z",
"recordRowId": "0198af4c-ffcc-44e6-bdcb-5567ba61a9e4",
"recordVersion": 1,
"data": {
"configId": "345940ed-2726-4b20-88aa-820857ac0e68",
"executionId": "345940ed-2726-4b20-88aa-820857ac0e68",
"executionVersion": 1,
"fileId": "345940ed-2726-4b20-88aa-820857ac0e68",
"recordId": "345940ed-2726-4b20-88aa-820857ac0e68",
"traceNumber": "example",
"version": 1,
"workflowId": "345940ed-2726-4b20-88aa-820857ac0e68"
},
"previous": null
}
Verifying Requests
Every webhook request includes two headers:
| Header | Description |
|---|---|
x-twisp-signature | HMAC SHA-256 signature of the request body, hex encoded. |
x-twisp-account-id | The Twisp tenant account identifier the event originated from. |
To validate that a request is authentic, recreate the signature with your endpoint's signingSecret and the raw request body, then compare it against the x-twisp-signature header. Reject any request where the signatures don't match.
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/http"
)
func validateRequest(r *http.Request, body []byte, signingSecret string) bool {
signature := r.Header.Get("x-twisp-signature")
mac := hmac.New(sha256.New, []byte(signingSecret))
mac.Write(body)
expectedSignature := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expectedSignature))
}
Delivery and Retries
Twisp sends an event to every endpoint whose subscription and filters match. If your endpoint responds with a 2XX HTTP status code, the delivery is considered successful.
On any other status code or connection error, Twisp retries the request every 471 seconds for up to 24 hours (roughly 185 attempts). Design your handler to be idempotent — use eventId to deduplicate, or recordRowId and recordVersion to detect out-of-order or duplicate deliveries.