Userflow
MARKETING · MARKETING
Users, groups, accounts, content sessions, and flows in the account they connected.
Acts as the person, not as itself
Each user connects their own account. Every call carries both identities — the agent and the person it is acting for — so the agent can never reach past what that individual can already do.
Credentials never touch the agent
Tokens live in the vault and attach server-side at call time. The agent holds a session, not a secret, and revoking access does not mean rotating a key.
Every call on the record
Who asked, which agent acted, which action ran, and the verdict that let it through — one audit trail across every integration, not one per vendor.
What an agent can do
Each action is granted on its own. An agent allowed to read is not thereby allowed to write, and the scope beside each row is what the acting user must have connected for it to run at all.
userflow_delete_accounts_by_account_id_invites_by_invite_idWRITEDelete an account invite via DELETE /accounts/{account_id}/invites/{invite_id}. Revokes a pending invitation so the link can no longer be accepted. It destroys an invitation rather than a person's access -- 'Remove an account member' is the one that revokes access someone already has. Verified 2026-09-24 by the round trip: after the delete the invite is gone from the collection AND answers 404 `not_found` at its own address. REQUIRES THE PERSONAL API KEY.
userflow_delete_accounts_by_account_id_members_by_member_idWRITERemove an account member via DELETE /accounts/{account_id}/members/{member_id}. REVOKES A COLLEAGUE'S ACCESS to the Userflow account. This is a Userflow TEAM member, not an end user -- 'Delete a user' is the end-user one. Getting it back means re-inviting them and them accepting. Removing the last owner/administrator can leave the account unadministrable, and Userflow enforces some of that itself (measured 2026-09-24: it answers 403 `access_denied` rather than letting the key owner act on their own membership). An unknown id answers 404 `not_found`. REQUIRES THE PERSONAL API KEY.
userflow_delete_content_sessions_by_content_session_idWRITEDelete a content session via DELETE /content_sessions/{content_session_id}. PERMANENT: it destroys one user's record of having seen a flow, together with any survey answers they gave in it. Deleting a session can make the content eligible to show that user again. USERFLOW'S OWN REFERENCE MISSPELLS THIS PATH as `/content_sesssions/` with three `s` characters; the service spells it with two, measured 2026-09-24 (three answers 404 `unknown_method_or_url`, two answers 200). IDEMPOTENT to the point of answering 200 `{"deleted": true}` for an id that never existed, so the reply proves nothing -- confirm by asking for the session, which answers 404 once it is gone.
userflow_delete_group_membershipsWRITERemove a user from a group via DELETE /group_memberships. Removes ONE user from ONE group and leaves both the user and the group intact. BOTH `user_id` and `group_id` are required and Userflow enforces it itself: measured 2026-09-24, calling this with neither answers 400 'Both user_id and group_id must be set as query parameters', so there is no unfiltered form that could empty a group. Idempotent -- a user who was not a member also answers 200 -- so confirm by listing the group's users again. There is no create/update counterpart on this path: memberships are written by embedding them in 'Create or update a user'.
userflow_delete_groups_by_group_idWRITEDelete a group via DELETE /groups/{group_id}. PERMANENT AND NOT REVERSIBLE: it destroys the group, its attributes, its memberships and its events. The USERS who were members survive. IDEMPOTENT -- measured 2026-09-24, an id that never existed also answers 200 `{"deleted": true}` -- so confirm by asking for the group again, which answers 404 once it is gone.
userflow_delete_users_by_user_idWRITEDelete a user via DELETE /users/{user_id}. PERMANENT AND NOT REVERSIBLE: it destroys the user, every attribute, every membership, every event and their whole flow history. Groups the user belonged to survive. IDEMPOTENT, which matters for how you check it: measured 2026-09-24 this answers 200 `{"deleted": true}` for an id that never existed, so the reply is NOT evidence the user was there. Confirm by asking for the user again -- a deleted user answers 404 `not_found`.
userflow_delete_webhook_subscriptions_by_webhook_subscription_idWRITEDelete a webhook subscription via DELETE /webhook_subscriptions/{webhook_subscription_id}. PERMANENT: deliveries stop immediately and THE SIGNING SECRET IS GONE WITH IT -- a replacement subscription gets a new one, so anything verifying signatures has to be updated. Use 'Update a webhook subscription' with `disabled: true` instead when the intent is to pause. IDEMPOTENT: measured 2026-09-24, deleting an already-deleted id also answers 200 `{"deleted": true}`, so confirm by asking for it (404 once gone) rather than by reading this reply.
userflow_get_accountsREADList accounts via GET /accounts. The Userflow ACCOUNTS (workspaces) the Personal API Key's owner is a member of -- this is Userflow's own organisation, not the customer's end users. Each carries its name, slug and whether 2FA is enforced; `environments` is null unless asked for with `expand`, and that expansion is the ONLY way to read an environment (measured 2026-09-24: /environments and /accounts/{id}/environments both answer 404 `unknown_method_or_url` -- there is no environments endpoint). REQUIRES THE PERSONAL API KEY: an environment key answers 401 `invalid_pak`.
userflow_get_accounts_by_account_idREADGet an account via GET /accounts/{account_id}. One Userflow account (workspace): name, slug, created_at and whether 2FA is enforced. Expand `environments` to read its environments -- their ids, names, slugs, region and which is primary -- because no standalone environments endpoint exists (measured 2026-09-24). REQUIRES THE PERSONAL API KEY.
userflow_get_accounts_by_account_id_invitesREADList account invites via GET /accounts/{account_id}/invites. Outstanding invitations to join the Userflow account: who was invited, by whom (`sender_id`), the role or permissions they would get, and whether the invite has expired. An accepted invite leaves this list and appears under members. REQUIRES THE PERSONAL API KEY.
userflow_get_accounts_by_account_id_invites_by_invite_idREADGet an account invite via GET /accounts/{account_id}/invites/{invite_id}. One pending invitation: name, email, sender, role, permissions and `is_expired`. A deleted or accepted invite answers 404 `not_found` (measured 2026-09-24). REQUIRES THE PERSONAL API KEY.
userflow_get_accounts_by_account_id_membersREADList account members via GET /accounts/{account_id}/members. The PEOPLE ON THE USERFLOW TEAM -- colleagues with a Userflow login -- not the customer's end users ('List users' on the Users API is those). Each member carries name, email, role (owner, admin, editor, viewer), a `permissions` array, and whether they have SSO, a password and 2FA. REQUIRES THE PERSONAL API KEY.
userflow_get_accounts_by_account_id_members_by_member_idREADGet an account member via GET /accounts/{account_id}/members/{member_id}. One Userflow team member: name, email, avatar, role, permissions, and the security facts (has_sso, has_password, is_2fa_enabled). An unknown id answers 404 `not_found` naming the member (measured 2026-09-24). REQUIRES THE PERSONAL API KEY.
userflow_get_attribute_definitionsREADList attribute definitions via GET /attribute_definitions. Every attribute name Userflow has seen in this environment, with its data type, scope and display name. Definitions are created automatically the first time an attribute is sent; there is no create, update or delete for them on this API. This is the tool to call before writing attributes, to find out what the environment already calls things.
userflow_get_contentREADList content via GET /content. 'Content' is Userflow's umbrella term for flows, checklists and launchers. Each item carries a `draft_version_id` and a `published_version_id`; the content ITSELF (steps, questions, tasks) lives in content versions. PASS ONLY A DOCUMENTED `type`: measured 2026-09-24, `type=flow` answers 200 while `type=nonsense` answers 500 `internal_error` rather than a 400, so an unsupported value looks like an outage. The three accepted values are checklist, flow and launcher.
userflow_get_content_by_content_idREADGet a content object via GET /content/{content_id}. One flow, checklist or launcher by id, with its name, type and the ids of its draft and published versions. The steps and copy are not here -- read the version, or expand it.
userflow_get_content_sessionsREADList content sessions via GET /content_sessions. A session is one user's journey through one flow, checklist or launcher: its progress, whether it completed, and any survey answers. Filter by `user_id` and `content_id` together to ask whether a specific user has seen a specific flow. Expand `answers` to read survey responses; each answer names its question by `question_cvid`, the cross-version id.
userflow_get_content_sessions_by_session_idREADGet a content session via GET /content_sessions/{session_id}. One user's journey through one piece of content, with `progress` as a decimal string (1 means fully completed), `completed`, `last_activity_at` and `is_preview`. Expand `answers` for the survey responses. An unknown id answers 404 `not_found` (measured 2026-09-24).
userflow_get_content_versionsREADList content versions via GET /content_versions. Userflow versions content: a new version is cut on edit and on publish, numbered incrementally per content object. `content_id` IS REQUIRED and Userflow enforces it -- measured 2026-09-24, omitting it answers 400 `invalid_params` 'You must filter by content_id via a query parameter'. There is no listing across content objects.
userflow_get_content_versions_by_version_idREADGet a content version via GET /content_versions/{version_id}. One version of a flow, checklist or launcher. Expand `questions` or `tasks` to see its survey questions or checklist tasks. WHEN YOU NEED A STABLE HANDLE, USE `cvid` AND NOT `id`: a question's or task's `id` is re-minted every time a new version is cut, while its Cross-Version Id stays the same for the logically same item -- and it is `cvid` that a content session's answers refer to.
userflow_get_event_definitionsREADList event definitions via GET /event_definitions. Every event name Userflow has seen in this environment, with its display name and description. Definitions appear automatically the first time an event is tracked; there is no create, update or delete for them here. Call this before 'Track an event' to reuse an existing name rather than minting a near-duplicate.
userflow_get_groupsREADList groups via GET /groups. Groups (companies, teams, departments) in this environment, cursor-paginated. `condition` is the same JSON-encoded filter shape as 'List users'. NOTE that Userflow gates the Groups feature by plan -- an account without it has no groups to list rather than an error.
userflow_get_groups_by_group_idREADGet a group via GET /groups/{group_id}. One group and its attributes. `users` and `memberships` are null unless asked for with `expand`. An unknown id answers 404 `not_found` (measured 2026-09-24).
userflow_get_usersREADList users via GET /users. The end users Userflow tracks in THIS environment -- the people using the customer's own application, not Userflow team members (those are 'List account members' on the Accounts API). Returns a cursor-paginated list object. `condition` filters on any attribute and must be a JSON-encoded string: measured 2026-09-24, a non-JSON value answers 400 `invalid_params` 'Invalid JSON provided in condition param'. Userflow rejects a condition matching more than 10,000 records, so this is a lookup rather than an export.
userflow_get_users_by_user_idREADGet a user via GET /users/{user_id}. One end user and all their attributes. `groups` and `memberships` are null unless asked for with `expand`. Measured 2026-09-24: an unknown id answers 404 `not_found` naming the id, so absence is a real answer rather than an empty object.
userflow_get_webhook_subscriptionsREADList webhook subscriptions via GET /webhook_subscriptions. The destinations Userflow POSTs notifications to for this environment, with their topics and whether each is disabled. THE SIGNING SECRET IS NOT HERE and that is Userflow's design, not a redaction: `secret` is returned only in the reply to 'Create a webhook subscription' (confirmed 2026-09-24 -- present on create, absent from both list and get). A lost secret means creating a new subscription.
userflow_get_webhook_subscriptions_by_webhook_subscription_idREADGet a webhook subscription via GET /webhook_subscriptions/{webhook_subscription_id}. One subscription: its url, topics, api_version and whether it is disabled. The signing secret is NOT in this reply -- Userflow returns it only at creation (measured 2026-09-24). An unknown id answers 404 `not_found`.
userflow_patch_accounts_by_account_id_members_by_member_idWRITEUpdate an account member via PATCH /accounts/{account_id}/members/{member_id}. Changes a Userflow TEAM MEMBER's role or permissions -- it does not touch an end user. Roles are admin, viewer and editor (Userflow names the set itself in its 400: 'Must be one of: admin, viewer, editor'); `owner` is not assignable here. THIS IS A PRIVILEGE CHANGE: granting admin hands someone the whole account, and demoting an administrator can lock work out. Userflow refuses some edits on its own -- measured 2026-09-24, patching the key owner's own membership answers 403 `access_denied` 'You do not have access to update this member' -- so a 403 here is a Userflow permission rule rather than a bad key. REQUIRES THE PERSONAL API KEY.
userflow_patch_webhook_subscriptions_by_webhook_subscription_idWRITEUpdate a webhook subscription via PATCH /webhook_subscriptions/{webhook_subscription_id}. Changes the url, the topics, the api_version or the disabled flag. `disabled: true` stops deliveries immediately and keeps the subscription and its secret, which is the reversible way to silence a webhook -- deleting it is not reversible. NOTE that `topics` REPLACES the list rather than adding to it, so send the full set you want.
userflow_post_accounts_by_account_id_invitesWRITECreate an account invite via POST /accounts/{account_id}/invites. INVITES A PERSON INTO THE USERFLOW ACCOUNT and emails them -- it grants access to a colleague, so it is an administrative act rather than a data write. Userflow names every requirement itself, measured 2026-09-24 by sending each incomplete body in turn: `name` is required ('Name is required'), then `email` ('Email is required'), then one of `role`/`permissions` ('Requires either role or permissions'), and an unknown role is refused with the accepted set ('Must be one of: admin, viewer, editor'). Note `member` is NOT a role. Revoke with 'Delete an account invite' before it is accepted. REQUIRES THE PERSONAL API KEY.
userflow_post_content_sessions_by_content_session_id_endWRITEEnd a content session via POST /content_sessions/{content_session_id}/end. Closes a session that is still running, so the user is no longer mid-flow. The session and its recorded answers are KEPT -- this ends it rather than deleting it, which is why it is not marked destructive; 'Delete a content session' is the one that destroys. Measured 2026-09-24: an unknown id answers 404 `not_found` naming the session, which is how this route was told apart from the misspelling in Userflow's own reference.
userflow_post_eventsWRITETrack an event via POST /events. Records something a user or a group did, for analytics and for segmenting and personalising flows. `name` is required and at least one of `user_id`/`group_id` must be given -- Userflow says so itself (measured 2026-09-24: no name -> 400 'Event name was missing.'; neither subject -> 400 'At least one of user_id and group_id must be given.'). `time` back-dates the event for historic imports; without it Userflow stamps the moment it arrives. Event and attribute names may contain only a-z, A-Z, 0-9, underscores, dashes and spaces. There is no GET on this path (404 `unknown_method_or_url`, measured): events are read through 'List event definitions' and through content sessions.
userflow_post_groupsWRITECreate or update a group via POST /groups. Create and update are one operation, keyed on `id`: a new id creates the group, an existing one merges the supplied attributes into it. Attribute values take the same literals and operation objects as a user's. A group can also be created as a side effect of 'Create or update a user' by embedding it in `groups` or `memberships`; this endpoint is how you touch one on its own.
userflow_post_usersWRITECreate or update a user via POST /users. ONE operation for both: a user whose `id` is not already in this environment is created, and one that is has the supplied attributes MERGED into what is already there -- attributes not named are left alone. `id` should be the id the user has in the customer's own database. An attribute value may be a literal or an operation object (`set`, `set_once`, `add`, `subtract`, `append`, `prepend`, `remove`, with an optional explicit `data_type`); an explicit null UNSETS that attribute. Groups can be attached in the same call through `groups` (simple) or `memberships` (when the membership itself carries attributes such as a role) -- only one of the two may be set. READ `prune_memberships` BEFORE SETTING IT: true DELETES every membership not named in this request, so it is only safe when the list is the user's complete set of groups. The groups themselves survive.
userflow_post_webhook_subscriptionsWRITECreate a webhook subscription via POST /webhook_subscriptions. Userflow starts POSTing matching notifications to `url` IMMEDIATELY. Both `url` and `topics` are required (measured 2026-09-24: 400 `invalid_params` "url: can't be blank." / "topics: can't be blank."). Topics are namespaced -- `user` matches user.created and user.updated, `event.tracked.<name>` matches one event, `*` matches everything. THE REPLY CONTAINS THE SIGNING SECRET (`whsec_...`) AND IT IS SHOWN ONLY HERE: it never appears in the list or get replies, so capture it now or create a new subscription later. It is what verifies the `Userflow-Signature` HMAC on every delivery, so treat the reply as credential material.
Often connected alongside
Put Userflow behind one governed endpoint.
Same permissions, same audit trail, whatever else you connect next.