PUT/sequences/{id}/graph

PUT /sequences/{id}/graph

Replace the steps and edges of a sequence in one call. This is how a sequence is built or repaired over the API.

View as Markdown

Request

curl -X PUT 'https://api.sendheron.com/api/v1/sequences/{id}/graph' \
  -H 'Authorization: Bearer <YOUR_API_KEY>' \
  -H 'Content-Type: application/json'

Required scopes

The key must carry these:

  • sequences:write

Path parameters

NameTypeRequiredDescription
iduuidYesSequence UUID.

Body parameters

NameTypeRequiredDescription
stepsobject[]Yes1 to 100 steps, upserted by id. The nested fields are documented below.
steps[].refstringYesClient-local reference for this step, used by edges. Any short string, unique within the payload, e.g. welcome-email.
steps[].idstringNoExisting step UUID to update in place, taken from GET /sequences/{id}/steps. Omit to create a new step. Steps that contacts have already passed through cannot be deleted, so repairs should carry existing ids.
steps[].typestringYesOne of TRIGGER, SEND_EMAIL, DELAY, CONDITION, ACTION.
steps[].configobjectNoStep configuration. The keys depend on type, and a key this step type does not read is REJECTED rather than stored: a config the worker cannot understand used to be accepted silently and simply never run. TRIGGER: triggerType (manual, contact_created, list_added, tag_added, event or webhook), plus tagId (UUID) for tag_added, listId (UUID) for list_added, eventName for event, and an optional source filter for contact_created. SEND_EMAIL: templateId (UUID). DELAY: duration (positive number) and unit (minutes, hours or days). A delay of one week is {"duration": 168, "unit": "hours"} or {"duration": 7, "unit": "days"}; there is no days key. CONDITION: field, operator, and value where the operator takes one. ACTION: actionType, plus tagId or tagName for tag actions, properties for property updates, webhookUrl (https) for webhooks, whose runtime rules are in the note below. Values are validated here; whether a step is COMPLETE is checked at activation, so a draft may still omit required keys.
steps[].xintegerNoCanvas X position.
steps[].yintegerNoCanvas Y position.
edgesobject[]YesUp to 200 edges, replaced wholesale rather than merged. The nested fields are documented below.
edges[].fromRefstringYesRef of the step this edge leaves.
edges[].toRefstringYesRef of the step this edge enters.
edges[].edgeTypestringNoOne of default, true, false. default for linear flow; true and false only from CONDITION steps.
expectedUpdatedAtISO-8601 datetimeNoThe sequence's updatedAt as you last read it. Supply it and a concurrent edit fails with 409 instead of being silently overwritten.
allowBranchRemovalbooleanNoConfirm removing a true or false branch edge that a CONDITION step had before this write. Without it such a payload is refused with 400 branchRemovalNeedsConfirmation: a one-branch condition is a valid graph, meaning "stop here", so the dropped branch would otherwise take effect silently and contacts taking that outcome would just stop. Retargeting a branch, meaning the same edgeType pointed at a different destination, needs no flag.

Responses

Returns 200 on success.

400
The payload failed validation, or the request is not valid for the current state. The body's `description` names the field and says what was wrong with it in words, e.g. "domain: domain must be a bare hostname such as acme.com, with no scheme, path, or port". The `error` code is stable and safe to branch on; `description` is for the human reading the log.
401
Missing or invalid API key.
403
Valid key, but it does not carry the required scope. The `error` code is `apiKeys.insufficientScopes` and the `description` names both halves of the problem: "Missing required scope(s): X. This key holds: Y." Neither is a secret, since the required scopes are on this page and the held ones are your own credential, and a bare "Forbidden" costs a debugging pass to work out which of the two it was.
404
No such record in this workspace. An id belonging to a different workspace returns this too, never a 403, because the API will not confirm that a record exists outside the workspace your key was issued in. Read it as "not yours or not there" rather than as "definitely gone".
409
A duplicate, or an idempotency key reused with a different payload.
429
Either rate-limit ceiling was exceeded.
400
Invalid graph by the same rules activation enforces, an active sequence, an unknown ref, a type change on an existing step, deleting a step with history, a step config carrying a key its type does not read, or removing a wired condition branch without `allowBranchRemoval`.
409
`expectedUpdatedAt` does not match, so somebody else changed the sequence since you read it.

Rate limits

100/min per key, counted against the organization's WRITE ceiling (400/min). Both windows are one minute. There is no hourly or daily quota.