Returning.AIDocs
v1

API reference / Workflow Builder

.md

Create or replace the trigger

Creates the workflow's single trigger, or replaces the existing one with the type and options you send.

Last updated 28 Oct 2026API v1

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_CONFLICT and 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.

FailureMeaningRecovery
400 VALIDATION_FAILEDUnknown trigger type, or options that do not match the typeRead 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#

workflowId#stringREQUIRED
Workflow identifier: 24 hexadecimal characters, as returned in _id.

Rule^[0-9a-fA-F]{24}$

Eg65f1a2b3c4d5e6f708192a3b

Headers#

Authorization#stringREQUIRED
Community API key: Bearer <API_KEY>.

EgBearer <API_KEY>

If-Match#stringOPTIONAL
Optional. Send the 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

Content-Type#stringREQUIRED
Request body format.

Ruleapplication/json

Body#

type#stringREQUIRED
Trigger type.

Ruletime, webhook, visit-community, join-community, custom-field-update, conditions-trigger, event-ingest

Eg"webhook"

name#stringOPTIONAL
Display name. Defaults to Trigger.

RuleMax 200 chars

Eg"Trade bonus trigger"

options#objectOPTIONAL
Trigger options, validated against the trigger type's schema from GET /catalog.

RuleNullable

Eg{"tokenExchangeEnabled": false, "apiKey": {"enabled": true}}

Watch for

  • apiKey.enabled: true on 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 any value you 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 webhook again. 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.
  • tokenExchangeEnabled and apiKey.enabled are 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. Send null, 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:

BASH
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:

BASH
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:

BASH
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:

JSON
{
  "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.

data#object
A workflow with its trigger, its steps in canvas order and its revision.

Eg{ ... }

_id#string
Workflow identifier.

Eg"65f1a2b3c4d5e6f708192a3b"

communityId#string
Community the workflow belongs to.

Eg"65f1a2b3c4d5e6f708192a50"

name#string
Workflow name.

Eg"VIP bonus"

status#string
draft, deployed or undeployed.

Eg"draft"

outgoingWebhookUrl#string
Failure webhook URL, when one is set.

RuleNullable

Egnull

createdAt#string
Creation time.

Eg"2026-10-24T09:15:00.000Z"

updatedAt#string
Last authored change.

Eg"2026-10-24T09:20:11.000Z"

trigger#object
The trigger, or null when the workflow has none.

RuleNullable

Eg{ ... }

_id#string
Trigger identifier. Use it as the source of a reference.

Eg"65f1a2b3c4d5e6f708192a41"

name#string
Display name.

Eg"Trade bonus trigger"

type#string
Trigger type.

Eg"webhook"

status#string
Trigger lifecycle state, for example idle.

Eg"idle"

options#object
Trigger options. A webhook key is shown only in the response that created it.

RuleNullable

Eg{ ... }

nodes#object[]
Main-line steps in canvas order, routers carrying their branches.

Eg[ ... ]

_id#string
Step identifier.

Eg"65f1a2b3c4d5e6f708192a3c"

name#string
Step name.

Eg"Read member XP"

type#string
Step type.

Eg"platform-action"

order#integer
Position on the main line, from 1.

Eg1

continueOnFailure#boolean
Whether a failure stops the run.

Egfalse

retryOnFailure#boolean
Whether the step is retried on failure.

Egfalse

options#object
Stored step options, with every credential replaced by ["__REDACTED__"].

RuleNullable

Eg{ ... }

status#string
Last run state of the step, when one exists.

Eg"success"

nestedNodes#object[]
Present on a router: each branch carries its own workflowNodeIds and nestedNodes.
revision#string
64 hexadecimal characters. Send it back as If-Match; it also arrives as the ETag header.

Eg"162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c"

redactedPaths#string[]
Every location where a credential was replaced by ["__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

400VALIDATION_FAILEDFix the request
Unknown trigger type, options that do not match the type, or a batch id path that is not a string. Fix the named field.
400REDACTED_VALUE_WITHOUT_STORED_SECRETFix the request
A marker arrived where nothing is stored, for example a copied export posted as a new trigger. Send the real value, or set the trigger again without the marker.
401AUTHENTICATION_REQUIREDFix the request
No key, or an unknown key. Send a valid key.
401API_KEY_REQUIREDFix the request
A valid admin session was used, because this API accepts community API keys only. Use a community API key.
403API_KEY_PERMISSION_DENIEDFix the request
No workflowBuilder. Grant the permission.
413VALIDATION_FAILEDFix the request
The request body is too large to read. Nothing was written. Send a smaller body; a large import is better split into a create plus step writes.
415UNSUPPORTED_MEDIA_TYPEFix the request
The body was not declared application/json. Send the header.

Fix the data03

404WORKFLOW_NOT_FOUNDFix the data
Unknown id, or another community's workflow. Use an id from this community.
409VERSION_CONFLICTFix the data
You sent an If-Match that is not the current revision. meta.currentRevision carries the live value. Re-read and resend.
409WORKFLOW_LOCKEDFix the data
Another write to this workflow held the lock for too long, and this write was not applied. Re-read and retry.

Retry with backoff03

429RATE_LIMITEDRetry with backoff
The community's API rate limit was reached. Wait for the window to reset, then retry.
500INTERNAL_ERRORRetry with backoff
Unexpected failure. Re-read the trigger before retrying. A retry of the same PUT cannot mint a second key, because the stored key is kept.
503LOCK_UNAVAILABLERetry with backoff
The write lock store did not answer, so nothing was written. Retry shortly.

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_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."
}

Next step#

Add a stepPOST/v1/workflow-builder/data-workflows/{workflowId}/nodes