Skip to main content
POST
Error

Authorizations

Authorization
string
header
required

A staff session token, uk_st_…. Minted by sign-up, sign-in or the two-factor exchange. Only a 401 means it is spent; not_a_member (403) is about the organization named in X-Organization-Id and leaves the token good for the others.

Headers

X-Organization-Id
string

The organization the caller is acting on — the org_… code that appears in the panel URL. It identifies; the membership JOIN is what authorizes, so a forged code reads nothing: the answer is not_a_member (403), which does not mean the session is over. Absent, the session's default organization answers, or — if that membership was revoked while the session was open — any other one the caller still holds.

Path Parameters

id
string<uuid>
required

Body

application/json
key
string
required

Lower case letters, digits, dot, dash or underscore.

Maximum string length: 80
title
string
required
Maximum string length: 200
kind
enum<string>
required

What satisfies the step.

event — a fact we publish about a contact. A closed list: a name that is not on it is refused, so this kind cannot become a step that never goes green.

product_event — an event your product sends through track(). Your vocabulary is open, so a name cannot be refused for not existing; only its shape is checked, and the step comes back with sightings so a typo is visible. This kind carries a weaker guarantee than the other two: analytics drops events under load and our own bus does not.

entitlement — a feature the contact's team holds, decided on every read rather than recorded.

Available options:
event,
product_event,
entitlement
event_type
string

Required for both event kinds. For event, one of the contact facts this system publishes (see event_types on the checklist read). For product_event, the exact name your product sends through track().

feature_key
string

Required when kind is entitlement. A feature key from your own catalogue.

scope
enum<string>
default:contact

Whether the step is satisfied for the PERSON or for their TEAM.

contact is the default and what every step meant before this field existed: one person, one step. customer is for the work that is the account's rather than anybody's — connecting a payment provider, importing a catalogue — and it is satisfied for everybody on the team as soon as any member does it, resolved on the read against the team the request is for.

Nothing is recorded for a team: the progress row still belongs to the person who did the work, and this asks a different question of the same rows. So the funnel counts TEAMS for a customer-scoped step and people for the rest — the step's scope comes back on the completion read for exactly that reason.

Refused on an entitlement step, which is already resolved against the team by construction. Frozen after creation, like kind: flipping it under people who already hold progress would change what their rows mean.

Available options:
contact,
customer
position
integer

Display order. Defaults to the end of the list.

sightings
integer

product_event steps only. How often this environment has seen that name in the last 90 days. Zero is the warning: your event vocabulary is open, so a name that does not exist cannot be refused, and a step nobody has ever sent is almost always a typo.

last_seen
string<date-time> | null

product_event steps only. When that name was last seen, or null for never.

action_url
string

Optional. Where the line sends somebody when they click it: a path starting with / (your own app) or an absolute http(s) URL.

Clicking never satisfies a step — kind decides that. A step with no destination is a line the reader has to go find the screen for themselves.

Maximum string length: 2048
action_label
string

Optional. The word on the control; without it the step's title becomes the link. Set alone, with no action_url, it declares an action your own page handles through the SDK's onStepAction — a control that does nothing until you wire that callback.

Maximum string length: 40
hint_pending
string | null

What it says on hover while it is NOT done — a reason or an instruction the title has no room for.

Two texts and not one because the same line means different things before and after: pending, the useful sentence is an instruction; done, an instruction is noise and what is useful is what it bought. A single field would force a sentence that is wrong half the time.

Maximum string length: 200
hint_done
string | null

What it says on hover once it IS done. Null is the common case and reads correctly — nothing on hover about a thing somebody already did.

Maximum string length: 200

Response

The step.

step
object

One line of the list. kind decides which of event_type and feature_key carries the rule; the other is always null. action_url and action_label are what happens when somebody clicks it, and neither has anything to do with what satisfies it.