Create or replace the trigger
Creates the workflow's single trigger, or replaces the existing one with the type and options you send.
- Method
- PUT
- Path
https://api.returning.ai / v1/ workflow-builder/ data-workflows/ {workflowId}/ trigger - Permission
- workflowBuilder
- Retries
- A PUT with the same body is safe only while the revision is unchanged; a retry against a changed workflow is refused with
409 VERSION_CONFLICTand writes nothing.
Authentication#
- Header
Authorization: Bearer <API_KEY>- Permission
- workflowBuilder
Authorization: Bearer <API_KEY> with workflowBuilder, on a workflow of the key's community. If-Match is optional: send the revision you read and a stale value is refused with 409 VERSION_CONFLICT, omit it and the change applies as the last write. On a deployed workflow the new trigger takes effect for the next event, immediately, with no extra header.
| Failure | Meaning | Recovery |
|---|---|---|
400 VALIDATION_FAILED | Unknown trigger type, or options that do not match the type | Read the trigger schema from GET /catalog and resend. |
Behaviour#
A workflow has exactly one trigger, so this is an upsert rather than an add.
A webhook trigger gets a generated key, and the response that mints it is the only response that shows the key; every later read replaces it with the redaction marker. There is no rotation: a PUT on an existing webhook trigger keeps the stored key and ignores an apiKey.value you send. Replacing an ingest or schedule trigger keeps that trigger's identity.
Request#
Path parameters#
_id.Rule^[0-9a-fA-F]{24}$
Eg65f1a2b3c4d5e6f708192a3b
Headers#
Bearer <API_KEY>.EgBearer <API_KEY>
revision the change is based on, bare or quoted, to avoid overwriting a concurrent change: a stale value is refused with 409 VERSION_CONFLICT. Omit it for last-write-wins.Eg162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c
Ruleapplication/json
Body#
Ruletime, webhook, visit-community, join-community, custom-field-update, conditions-trigger, event-ingest
Eg"webhook"
Trigger.RuleMax 200 chars
Eg"Trade bonus trigger"
GET /catalog.RuleNullable
Eg{"tokenExchangeEnabled": false, "apiKey": {"enabled": true}}
Watch for
apiKey.enabled: trueon a trigger that has no stored key asks the platform to mint one. On a trigger that already has a key, the stored value is kept and anyvalueyou send is ignored; an empty string is refused.- To get a different key, remove the webhook type first: set the trigger to another type, then set
webhookagain. No key is stored in between, so the second call mints a new one and shows it once. The trigger id is kept, but this is a replacement of the trigger's authentication, not a rotation, and the sender must be reconfigured. tokenExchangeEnabledandapiKey.enabledare booleans and are never redacted. They tell you the authentication mode without exposing the credential.- To keep a stored key while changing something else, send the redaction marker back at that path. A marker at a path with no stored value is
400 REDACTED_VALUE_WITHOUT_STORED_SECRET. - The ingest batch id path is read from the root of the delivered event. A per-row batch id is not found, and the ingest fails before it runs.
- Record id paths are independent checks. A repeat of any one of them marks the row a duplicate, and the path name is not part of the stored identity.
- A trigger type that takes no options is stored with
options: null. Sendnull, not an empty object. - Changing the trigger type on a deployed workflow changes what fires it from the next event, immediately. There is no second version of the workflow, so a sender configured for the old type simply stops producing runs.
More examples and details
An ingest trigger with a batch id path and per-row record id paths:
curl --request PUT \
--url "https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b/trigger" \
--header "Authorization: Bearer <API_KEY>" \
--header "Content-Type: application/json" \
--header "If-Match: 162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c" \
--data '{"type":"event-ingest","name":"Trades","options":{"batchIdPath":"metadata.batch_id","recordIdPaths":["trade_id","order_id"]}}'
A webhook trigger whose key the platform mints:
curl --request PUT \
--url "https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b/trigger" \
--header "Authorization: Bearer <API_KEY>" \
--header "Content-Type: application/json" \
--header "If-Match: 162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c" \
--data '{"type":"webhook","name":"Trade bonus trigger","options":{"tokenExchangeEnabled":false,"apiKey":{"enabled":true}}}'
A daily schedule:
curl --request PUT \
--url "https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b/trigger" \
--header "Authorization: Bearer <API_KEY>" \
--header "Content-Type: application/json" \
--header "If-Match: 162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c" \
--data '{"type":"time","name":"Every night","options":{"type":"day","time":2,"timezone":"UTC"}}'
The response that mints a webhook key:
{
"data": {
"_id": "65f1a2b3c4d5e6f708192a3b",
"communityId": "65f1a2b3c4d5e6f708192a50",
"name": "VIP bonus",
"status": "draft",
"outgoingWebhookUrl": null,
"createdAt": "2026-10-24T09:15:00.000Z",
"updatedAt": "2026-10-24T12:20:02.000Z",
"trigger": {
"_id": "65f1a2b3c4d5e6f708192a41",
"name": "Trade bonus trigger",
"type": "webhook",
"status": "idle",
"options": { "tokenExchangeEnabled": false, "apiKey": { "enabled": true, "value": "<WEBHOOK_KEY>" } }
},
"nodes": [],
"revision": "68f78253046393414dec404e5442c140bb5db0393a7d1c389ff57bba71585501",
"redactedPaths": []
}
}
curl --request PUT \
--url 'https://api.returning.ai/v1/workflow-builder/data-workflows/<workflowId>/trigger' \
--header 'Authorization: Bearer <API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"type": "webhook",
"name": "Trade bonus trigger",
"options": {
"tokenExchangeEnabled": false,
"apiKey": {
"enabled": true
}
}
}'
Response#
HTTP 200 with the whole workflow and its new revision. A readback is the response itself, plus one more GET /{workflowId} to confirm the key is now redacted and the type and options are stored as sent.
trigger._id is what step options reference when a step reads the trigger output. It stays the same when the options change and changes only if the trigger is recreated.
Eg{ ... }
Eg"65f1a2b3c4d5e6f708192a3b"
Eg"65f1a2b3c4d5e6f708192a50"
Eg"VIP bonus"
draft, deployed or undeployed.Eg"draft"
RuleNullable
Egnull
Eg"2026-10-24T09:15:00.000Z"
Eg"2026-10-24T09:20:11.000Z"
null when the workflow has none.RuleNullable
Eg{ ... }
Eg"65f1a2b3c4d5e6f708192a41"
Eg"Trade bonus trigger"
Eg"webhook"
idle.Eg"idle"
RuleNullable
Eg{ ... }
Eg[ ... ]
Eg"65f1a2b3c4d5e6f708192a3c"
Eg"Read member XP"
Eg"platform-action"
Eg1
Egfalse
Egfalse
["__REDACTED__"].RuleNullable
Eg{ ... }
Eg"success"
workflowNodeIds and nestedNodes.If-Match; it also arrives as the ETag header.Eg"162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c"
["__REDACTED__"].Eg[]
{
"data": {
"_id": "65f1a2b3c4d5e6f708192a3b",
"communityId": "65f1a2b3c4d5e6f708192a50",
"name": "VIP bonus",
"status": "draft",
"outgoingWebhookUrl": null,
"createdAt": "2026-10-24T09:15:00.000Z",
"updatedAt": "2026-10-24T09:20:11.000Z",
"trigger": {
"_id": "65f1a2b3c4d5e6f708192a41",
"name": "Trade bonus trigger",
"type": "webhook",
"status": "idle",
"options": {
"tokenExchangeEnabled": false,
"apiKey": {
"enabled": true,
"value": "<WEBHOOK_KEY>"
}
}
},
"nodes": [
{
"_id": "65f1a2b3c4d5e6f708192a3c",
"name": "Read member XP",
"type": "platform-action",
"order": 1,
"continueOnFailure": false,
"retryOnFailure": false,
"options": {
"action": "get-user-fields",
"selectUser": {
"type": "email",
"value": ["{{65f1a2b3c4d5e6f708192a41.email}}"]
},
"fields": [
{
"label": "Total XP",
"field": "total_xp"
}
]
}
}
],
"revision": "162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c",
"redactedPaths": []
}
}
Errors#
Fix the request07
VALIDATION_FAILEDFix the requestREDACTED_VALUE_WITHOUT_STORED_SECRETFix the requestAUTHENTICATION_REQUIREDFix the requestAPI_KEY_REQUIREDFix the requestAPI_KEY_PERMISSION_DENIEDFix the requestworkflowBuilder. Grant the permission.VALIDATION_FAILEDFix the requestUNSUPPORTED_MEDIA_TYPEFix the requestapplication/json. Send the header.Fix the data03
WORKFLOW_NOT_FOUNDFix the dataVERSION_CONFLICTFix the dataIf-Match that is not the current revision. meta.currentRevision carries the live value. Re-read and resend.WORKFLOW_LOCKEDFix the dataRetry with backoff03
RATE_LIMITEDRetry with backoffINTERNAL_ERRORRetry with backoffLOCK_UNAVAILABLERetry with backoffRetries
A PUT with the same body is safe only while the revision is unchanged; a retry against a changed workflow is refused with 409 VERSION_CONFLICT and writes nothing. A retry after an ambiguous failure is the risky case: if the first call minted a webhook key, it was shown only in that lost response. Reading the workflow afterwards shows ["__REDACTED__"], and sending a value does not replace it. A lost key is not recoverable by retrying; plan for the replacement path below.
{
"meta": {
"status": "error",
"statusCode": 400,
"code": "VALIDATION_FAILED",
"errors": [
{
"path": "params.workflowId",
"message": "Id must be a 24 character hex string"
}
],
"requestId": "3f4d0e8b-7a6c-4f1e-9b2d-5a7c8e1f0a3b"
},
"message": "API request rejected error.",
"detail": "The request body is not valid.",
"solution": "Read error detail and try again."
}