NAV Navbar

Userflow API Reference

The Userflow REST API allows you to integrate Userflow with your application and manage your account programmatically. The API is based on REST. Its URLs are resource-oriented and it uses the standard HTTP verbs (GET, POST etc.). It receives and responds with JSON. It responds with standard HTTP response codes.

Userflow offer two different sets of REST APIs:

Follow us on X for API announcements.

Users API

Users API endpoint -

For environments in US region :

  https://api.userflow.com

For environments in EU region :

  https://api.eu.userflow.com

Users API can be used for managing users in your application (including their attributes, groups, and events). This is the core API for integrating Userflow into your app to track and personalize experiences for the people using your product. With the Users API, you can synchronize data with Userflow and track events directly from your back-end application. The Users API uses environment-scoped API keys for authentication.

Accounts API

Accounts API endpoint -

https://api.userflow.com

Accounts API can be used for managing your Userflow account, including team members, invites, and account settings. This is designed for automating team management and account-level operations (e.g., inviting members to your Userflow team). The Accounts API uses personal API keys tied to your Userflow member login for authentication.

Note: Terms like User refer to your users using your application, while Member refers to users of your Userflow account.

Quick links

Quick access to endpoints that you'll probably need:

Users API

Accounts API

Authentication

You use API keys to authenticate your API requests. Users API and Accounts API use different sets of API keys for authentication. Make sure to keep your API keys safe and secret. Do not put them in your GitHub repo or in your client-side code.

All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.

Users API authentication

Include your API key in the Authorization header prefixed with "Bearer ":

curl https://api.userflow.com/users \
  -H 'Authorization: Bearer <env-api-key>'

Example:

curl https://api.userflow.com/users \
  -H 'Authorization: Bearer ak_2snys2ycbvetjeivbzs7m7btfm'

You can use environment API keys to authenticate Users API endpoints. These keys are tied to an environment in your account and grant access to your whole environment. You can view and create your environment API keys in the Userflow web app under Settings -> API. Note that if you have multiple environments (e.g. Production and Staging) that each environment has separate API keys, and that an API key only authorize access to its environment.

Accounts API authentication

Include your API key in the Authorization header prefixed with "Bearer ":

curl https://api.userflow.com/accounts \
  -H 'Authorization: Bearer <personal-api-key>'

Example:

curl https://api.userflow.com/accounts \
  -H 'Authorization: Bearer pak_3rmse5xbarsgkauzose8n6asdb'

You can use personal API keys to authenticate Accounts API requests. These keys are tied to your individual member profile (your Userflow login) and grant access across accounts you belong to based on your roles and permissions tied to a particular account. You can view and create personal API keys in the Userflow web app under My Account -> Personal API Keys.

Versioning

Override API version (recommended!):

curl https://api.userflow.com/users \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

When we make backwards-incompatible changes to the API, we release new, dated versions. The current (and so far only) version is 2020-01-03.

All requests will default to using the latest API version, unless you explicitly override the API version with the Userflow-Version header.

We therefore strongly recommend that you specify your desired version to make sure your integration won't break in the future.

Rate limits

The API employs 2 safeguards against bursts of incoming traffic to help maximize its stability:

Errors

HTTP status codes

The API responds with HTTP status codes in the 2xx range when requests are successful. If a request is invalid due to information provided (e.g. a missing parameter, or), it'll respond with 4xx codes. If Userflow experiences an internal error, it'll respond with 5xx errors.

Code Meaning
200 OK - Request was successful
400 Bad Request - Invalid query parameters or JSON body. Check the error response for details on how to fix.
401 Unauthorized - Missing or invalid API key.
403 Forbidden - Access denied. Check the error response for more details.
404 Not Found - Unrecognized URL.
405 Method Not Allowed - The used HTTP method (e.g. GET) is not allowed for the given URL.
406 Not Acceptable - You requested a format that isn't JSON.
415 Unsupported Media Type - You probably forgot to add Content-Type: application/json to a POST request.
429 Too Many Requests - You have sent too many requests, too quickly, and are being rate limited. See Rate limits.
500 Internal Server Error - Something went wrong on Userflow's end. Consider retrying the request, or reach out to Userflow for help.
503 Internal Server Error - Service Unavailable - We're temporarily offline for maintenance. Please try again later.

Error responses

Example error response:

{
  "error": {
    "code": "invalid_api_key",
    "message": "Invalid API key provided: ...",
    "doc_url": "https://userflow.com/docs/api"
  }
}

Error responses contains an error object with the following properties:

Key Description
code String - Machine-readable error code.
message String - Human-readable explanation of what went wrong.
doc_url String - URL to the Userflow API docs.
request_id String - Userflow assigns all requests an ID. When you report issues to us, including this request ID will make it easier for us to see what happened.

Pagination

All top-level API resources that support listing (such as listing users) use the same format for pagination and the same response object.

Pagination query parameters

Listing uses cursor-based pagination through 2 query parameters: limit and starting_after.

Key Description
limit Number - Tells Userflow how many items to respond with. Must be between 1 and 100. Defaults to 10.
starting_after String - Tells Userflow to respond with items in the list immediately after and not including the object with the given id. If you want the next page, use the id of the last item in the previous request. It's often easiest to use the list object's next_page_url field though. If starting_after is not set, Userflow will return items from the beginning of the list.

The list object

Example list object (from GET /users):

{
  "object": "list",
  "data": [
    {
      "id": "1",
      "object": "user",
      ...
    },
    ...
    {
      "id": "10",
      "object": "user",
      ...
    }
  ],
  "has_more": true,
  "url": "/users",
  "next_page_url": "/users?starting_after=10"
}

All list endpoints returns an object with the following keys:

Key Description
object String - Represents the object's type. Always list.
data Array - An array containing the actual response objects (e.g. users). No more than limit elements will be included.
has_more Boolean - true if there are more items in the next page, false if this is the last page.
url String - The URL for this list. This will include any pagination parameters, filters or order-by parameters.
next_page_url String - URL to fetch the next page from. Will always be set, even if has_more is false. This is because there may not currently be more items in the list, but some clients may still want to know how to fetch the next page if more items were to arrive later.

Ordering

Order by single field:

GET /users?order_by=attributes.name

Order by multiple fields:

GET /users?order_by[]=attributes.name&order_by[]=created_at

Use descending order:

GET /users?order_by=-created_at

Some top-level API resources that support listing (such as listing users) can be ordered by one or more fields using a sort_by query parameter. Each resource will list which fields it can be ordered by.

Key Description
order_by String or array of strings - A single field or a an array of fields to order by. If you include multiple fields, the list will be sorted by the first element first, then the second, and so on. Each field is sorted in ascending order, unless you prefix it with a - (hyphen), in which case it'll be sorted in descending order.

Expanding objects

Get a user with all their memberships and groups/companies:

curl https://api.userflow.com/users/1?expand=memberships.group \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "1",
  "object": "user",
  "attributes": {
    "name": "Maxie Kris"
  },
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "memberships": [
    {
      "id": "e9b32bd0-63cb-415e-9c4f-477c85b92f97",
      "object": "group_membership",
      "attributes": {
        "role": "admin"
      },
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "group": {
        "id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
        "object": "group",
        "attributes": {
          "name": "Acme Inc.",
          "billing_plan": "plus"
        },
        "created_at": "2022-10-17T12:34:56.000+00:00"
      },
      "group_id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
      "user": null,
      "user_id": "1"
    }
  ]
}

You can ask for objects or lists to be expanded in responses from the API using the expand query parameter. For example, an event object has a user_id, which simply contains the string ID of the user the event is related to. By adding ?expand=user to the request URL, the API will respond with an extra user key which contains the full user object.

Expansion also works with has-many relationships, such as a user's list of memberships. By default, the memberships key will be null (not expanded). If you add ?expand=memberships, the API will respond with an array of all the user's memberships.

You can expand recursively by specifying nested fields separated with a dot (.). Example to get memberships and their groups: ?expand=memberships.group. You can expand to a maximum of 4 levels deep.

You can use the expand parameter on any endpoint which returns expandable fields, including list, create, and update endpoints.

You can expand multiple objects at once by adding multiple paths in the expand array. Example: ?expand[]=user&expand[]=group.

Attributes

Example user object with attributes:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "object": "user",
  "attributes": {
    "email": "annabelle@example.com",
    "email_verified": true,
    "name": "Annabelle Terry",
    "project_count": 17,
    "signed_up_at": "2022-09-29T12:34:56.000+00:00"
  }
  // ...
}

Some objects from Users API, such as users, groups and events, can hold custom attributes in the free-form attributes dictionaries.

Attribute naming

Attribute names (the keys in attributes dictionaries) can only consist of a-z, A-Z, 0-9, underscores, dashes and spaces.

We recommend using snake_case everywhere though.

Attributes' human-friendly display names (e.g. "Signed Up" for signed_up_at) can also be configured in the Userflow UI.

Attribute data types

We support the following attribute data types:

Name Description
string Represents a string.
number Represents a number (supports both integers and floating point numbers).
boolean Represents either true or false.
datetime Represents a point in time, always stored as ISO 8601 in UTC.
list Represents a list of strings.

Attribute values and operations

Explicitly specify data type:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "phone": {"set": 12345678, "data_type": "string"}
  }
}

Set attribute, unless it has already been set:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "coupon_code": {"set_once": "xyz123"}
  }
}

Increment attribute:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "widget_count": {"add": 1},
    "total_revenue": {"add": 1234.56}
  }
}

Decrement attribute:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "days_left": {"subtract": 1}
  }
}

Append a single value to a list:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "foods": {"append": "apple"}
  }
}

Append a multiple values to a list:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "foods": {"append": ["apple", "banana"]}
  }
}

Prepend values to a list:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "foods": {"prepend": ["apple", "banana"]}
  }
}

Remove values from a list:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "foods": {"remove": ["apple", "banana"]}
  }
}

Unset attribute:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "attributes": {
    "remove_me": null
  }
}

When updating objects with attributes, the values in the attributes dictionary can be literal values (string, boolean, number), which just sets the attributes to the given values.

If an attribute value is null, the attribute will be unset/removed from the user.

You can also specify an operation object instead of a literal value. This operation object can contain the following keys (note that exactly one of set, set_once, add, subtract, append, prepend or remove must be set):

Key Description
set String/number/boolean - Sets the attribute to this value. This is the same as using literal values, except this way you can explicitly set the data_type, too.
set_once String/number/boolean - Sets the attribute to this value, but only if the user doesn't already have a value for this attribute.
add Number - Adds this value to the attribute's current value. If the user does not have the attribute yet, the given number will be added to 0. Only works on number attributes.
subtract Number - Same as add, but subtracts the given number instead. To subtract, you either supply a negative number to add, or a positive number to subtract.
append String/list of strings - Adds one or more values to the end of a list type attribute. If the user's attribute value was not set before, it will be initialized to an empty list before the values are appended. Any values already present in the user's attribute value will be ignored (no duplicates are inserted).
prepend String/list of strings - Like append, but inserts the values at the beginning of the list.
remove String/list of strings - Removes one or more values from a list type attribute. If the user's attribute value was not set before, it will be set to an empty list.
data_type String - Explicitly tells Userflow what data type to use. See Attribute data types for possible values. You usually don't have to set this, as Userflow will infer the right data type depending on the values you supply.

Condition filtering

Using the condition parameter when listing users:

curl https://api.userflow.com/users?condition=%7B%22type%22%3A%22attribute%22%2C%22attribute_name%22%3A%22plan%22%2C%22operator%22%3A%22eq%22%2C%22value%22%3A%22Pro%22%7D \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Simple condition filtering on a user attribute:

{
  "type": "attribute",
  "attribute_name": "experience_level",
  "operator": "eq",
  "value": "Expert"
}

Simple condition filtering on a group attribute:

{
  "type": "attribute",
  "attribute_name": "group/plan",
  "operator": "eq",
  "value": "Pro"
}

Advanced condition matching users who are both on the Pro plan and has created at least 10 widgets:

{
  "type": "clause",
  "operator": "and",
  "conditions": [
    {
      "type": "attribute",
      "attribute_name": "plan",
      "operator": "eq",
      "value": "Pro"
    },
    {
      "type": "attribute",
      "attribute_name": "widget_count",
      "operator": "gte",
      "value": 10
    }
  ]
}

When listing users and listing groups in Users API, you can provide a ?condition query parameter to filter the list based on attributes.

The condition parameter must be a JSON-formatted string.

You can perform AND and OR logic using multiple conditions combined in a conditions list.

When referring to attribute names in "type": "attribute" conditions, the attribute_name defaults to refer to a user attribute, e.g. experience_level. If you want to refer to a group attribute, such as plan, then you must prefix it like group/plan. If you want to refer to a membership attribute, such as role, then you must prefix it like group_membership/role.

Note that condition filtering is intended to find a small number of matching users. If the condition matches more than 10,000 records, the request will be rejected.

We support the following types of conditions.

and - AND clause

{
  "type": "clause",
  "operator": "and",
  "conditions": [
    {
      // ...condition
    },
    {
      // ...condition
    }
  ]
}

Used to combine multiple child conditions with AND.

True if all of the child conditions are true.

between - Is between

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "between",
  "value": 10,
  "value2": 20
}

True if the given number attribute is between value and value2 (both inclusive).

contains - Contains

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "contains",
  "value": "my value"
}

True if the given string attribute contains the string value.

empty - Is empty

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "empty"
}

True if the given attribute is either not set, null, an empty string or an empty list.

ends_with - Ends with

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "ends_with",
  "value": "my value"
}

True if the given string attribute ends with value.

eq - Equals / is

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "eq",
  "value": "my value"
}

True if the given attribute equals value.

excludes_all - Does not include all of

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "excludes_all",
  "values": ["apple", "banana"]
}

True if the given list attribute does not include all of the given values (an array of strings).

excludes_any - Does not include at least one of

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "excludes_any",
  "value": "my value"
}

True if the given list attribute does not include at least one of the given values (an array of strings).

false - Is false

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "false"
}

True if the given boolean attribute is false.

gt - Is greater than

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "gt",
  "value": 123
}

True if the given number attribute is greater than value.

gte - Is greater than or equal to

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "gte",
  "value": 123
}

True if the given number attribute is greater than value.

includes_all - Includes all of

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "includes_all",
  "values": ["apple", "banana"]
}

True if the given list attribute includes all of the given values (an array of strings).

includes_any - Includes at least one of

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "includes_any",
  "values": ["apple", "banana"]
}

True if the given list attribute includes at least one of the given values (an array of strings).

lt - Is less than

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "lt",
  "value": 123
}

True if the given number attribute is less than value.

lte - Is less than or equal to

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "lte",
  "value": 123
}

True if the given number attribute is less than or equal to value.

ne - Is not / does not equal

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "ne",
  "value": "my value"
}

True if the given attribute does not equal value.

not_contains - Does not contain

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "not_contains",
  "value": "my value"
}

True if the given string attribute does not contain value.

not_empty - Has any value / is not empty

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "not_empty"
}

True if the given attribute is set and is neither an empty string, nor null nor an empty list.

or - OR clause

{
  "type": "clause",
  "operator": "or",
  "conditions": [
    {
      // ...condition
    },
    {
      // ...condition
    }
  ]
}

Used to combine multiple child conditions with OR.

True if at least one of the child conditions is true.

starts_with - Starts with

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "starts_with",
  "value": "my value"
}

True if the given string attribute starts with value.

true - Is true

{
  "type": "attribute",
  "attribute_name": "my_attribute",
  "operator": "true"
}

True if the given boolean attribute is true.

Users

Users are the people using your application. Userflow keeps track of your users, so we can determine which flows to show them, and remember which flows they've already seen.

Your application can either register users with Userflow and update their attributes on the client-side using Userflow.js' userflow.identify() method, or directly from your back-end using this API.

The user object

Example user object:

{
  "id": "34907ae0-24e0-4261-ac31-3c7299a354c0",
  "object": "user",
  "attributes": {
    "email": "annabelle@example.com",
    "email_verified": true,
    "name": "Annabelle Terry",
    "project_count": 17,
    "signed_up_at": "2022-09-29T12:34:56.000+00:00"
  },
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "groups": null,
  "memberships": null
}
Key Description
id String - Unique identifier for the user. Should match the ID the user has in your database.
object String - Represents the object's type. Always user.
attributes Object - A map with all the user's attributes. You can add any attributes you want here.
created_at String - ISO 8601 date time representing when the user was created with Userflow. Note that this is not the time the user signed up in your app - use an attribute such as signed_up_at in the attributes object for that.
groups Array of group objects - A list of all the groups the user is a member of. This field is only useful if you're not interested in membership attributes, in which case you should use memberships. Defaults to null, but can be expanded using ?expand=groups.
memberships Array of group membership objects - A list of all the user's group memberships. Memberships can hold attributes that describe the user's role for just this group (e.g. their access level). Each membership object will point to an actual group object. Defaults to null, but can be expanded using ?expand=memberships, or ?expand=memberships.group if you want to expand both the user's memberships and each group.

Create or update a user

POST /users

curl https://api.userflow.com/users \
  -XPOST \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
        "attributes": {
          "name": "Evelyn Reichert",
          "email": "evelyn@example.com",
          "signed_up_at": "2022-09-29T12:34:56.000+00:00"
        }
      }'

Response:

{
  "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
  "object": "user",
  "attributes": {
    "email": "evelyn@example.com",
    "name": "Evelyn Reichert",
    "signed_up_at": "2022-09-29T12:34:56.000+00:00"
  },
  "created_at": "2022-10-17T12:34:56.000+00:00"
}

Creating and updating a user is the same operation. If a user with the same ID doesn't already exist in Userflow, it will be created. If it already exists, the given attributes will be merged into the existing user's attributes.

Request body

Key Description
id* String - Unique identifier for the user. Should match the ID the user has in your database.
attributes Object - A map with attributes to update for the user. You can add any attributes you want here. Existing attributes not included in the request will not be touched. Attribute values can either be supplied as literal values (string, boolean, number) or as operations (set, set_once, add, subtract). See Attributes.
groups** Array of group objects - A list of groups/companies to update and ensure the user is a member of. This field is simpler to use than memberships, if you don't use membership attributes.
memberships** Array of group membership objects - A list of group/company memberships to create/update for the user. Memberships can hold attributes that describe the user's role for just this group (e.g. their access level). Each membership object must include an embedded group object with the group's id as a minimum.
prune_memberships Boolean - By default, the API will only update the memberships/groups that's included in the request. Existing memberships that are not included will not be removed, unless you set prune_memberships to true. Only set prune_memberships to true, if the groups or memberships list is set and contains all the groups the user belongs to. When a membership is deleted, the group itself is left intact.

* Required
** Only one of groups and memberships can be set.

Response body

Returns the created/updated user object.

Create or update a user with groups

Create/update user including groups/companies (not using memberships)

curl https://api.userflow.com/users \
  -XPOST \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
        "attributes": {
          "name": "Evelyn Reichert",
        },
        "groups": [
          {
            "id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
            "attributes": {
              "name": "Acme Inc.",
              "billing_plan": "plus",
            }
          }
        ]
      }'

You can create/update a user and associate it with one or more groups/companies in one go by embedding a list of groups.

If you want to assign attributes to the user's membership of the group (e.g. their access level), use the memberships key instead (see next section).

Create or update a user with memberships

Create/update user including group/company memberships

curl https://api.userflow.com/users \
  -XPOST \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c",
        "attributes": {
          "name": "Evelyn Reichert",
        },
        "memberships": [
          {
            "attributes": {
              "role": "admin"
            },
            "group": {
              "id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
              "attributes": {
                "name": "Acme Inc.",
                "billing_plan": "plus",
              }
            }
          }
        ]
      }'

You can create/update a user, associate it with one or more groups/companies and assign attributes to the user's membership of each group in one go by embedding a list of memberships.

Memberships can hold attributes that describe the user's role for just this group (e.g. their access level).

Get a user

GET /users/:user_id

curl https://api.userflow.com/users/50c3d110-80f0-47c8-8364-9adfcdb2c9fa \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "50c3d110-80f0-47c8-8364-9adfcdb2c9fa",
  "object": "user",
  "attributes": {
    "email": "nathanial@example.com",
    "name": "Nathaniel Harvey",
    "signed_up_at": "2022-09-29T12:34:56.000+00:00"
  },
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "groups": null,
  "memberships": null
}

Retrieves a user including all their attributes.

URL arguments

Key Description
user_id* String - Unique identifier for the user. Should match the ID the user has in your database.

* Required

Response body

Returns the user object, if found. Responds with 404 Not Found if not.

Delete a user

DELETE /users/:user_id

curl https://api.userflow.com/users/4c20f0fb-a835-4f3c-b726-8c34bf538dcc \
  -XDELETE \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "4c20f0fb-a835-4f3c-b726-8c34bf538dcc",
  "object": "user",
  "deleted": true
}

Permanently deletes a user including all their attributes, memberships, events and flow history. It cannot be undone. Groups that the user was a member of will be left intact.

URL arguments

Key Description
user_id* String - Unique identifier for the user. Should match the ID the user has in your database.

* Required

Response body

Returns an object with a deleted key on success (or if the user already didn't exist - this call is idempotent).

List users

GET /users

curl https://api.userflow.com/users \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "1",
      "object": "user",
      "attributes": {
        "email": "maxie@example.com",
        "name": "Maxie Kris",
        "signed_up_at": "2022-09-29T12:34:56.000+00:00"
      },
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "groups": null,
      "memberships": null
    },
    ...
    {
      "id": "10",
      "object": "user",
      ...
    }
  ],
  "has_more": true,
  "url": "/users",
  "next_page_url": "/users?starting_after=10"
}

Filter by email:

curl https://api.userflow.com/users?email=alice@example.com \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Returns a list of your users.

Query parameters

Key Description
condition String - Only include users that match the given condition. Can be used to filter any attribute. See Condition filtering.
email String - Only include users whose email attribute equals this value.
group_id String - Only include users who are members of this group.
limit Number - See Pagination.
order_by String or array of strings - See Ordering. Fields that users can be ordered by: created_at (note that this is when the user was created with Userflow), attributes.signed_up_at, attributes.last_seen_at, attributes.name. Defaults to created_at.
segment_id String - Only include users in this segment. Find a segment's ID in the Userflow UI via the three-dot menu at the top right of the segment page.
starting_after String - See Pagination.

Response body

Returns a list object with a data property that contains an array of user objects.

Groups/companies

Groups are used to group multiple users together. In your business, groups may correspond to e.g. companies, teams or departments.

Like users, groups can have attributes. Events can also be associated with groups.

With groups, you can orchestrate the flows a user sees:

Note that the Groups feature is only available in certain plans. Check your Billing page or reach out to us to ask.

The group object

Example group object:

{
  "id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
  "object": "group",
  "attributes": {
    "name": "Acme Inc.",
    "billing_plan": "plus",
    "signed_up_at": "2022-09-29T12:34:56.000+00:00"
  },
  "created_at": "2022-10-17T12:34:56.000+00:00"
}
Key Description
id String - Unique identifier for the group/company. Should match the ID the group/company has in your database.
object String - Represents the object's type. Always group.
attributes Object - A map with all the group's attributes. You can add any attributes you want here.
created_at String - ISO 8601 date time representing when the group was created with Userflow. Note that this is not the time the group was signed up in your app - use an attribute such as signed_up_at in the attributes object for that.
memberships Array of group membership objects - A list of all the group's memberships. Memberships can hold attributes that describe each user's role for just this group (e.g. their access level). Each membership object will point to an actual user object. Defaults to null, but can be expanded using ?expand=memberships, or ?expand=memberships.user if you want to expand both the group's memberships and each user.
users Array of user objects - A list of all users that are members of this group. This field is only useful if you're not interested in membership attributes, in which case you should use memberships. Defaults to null, but can be expanded using ?expand=users.

Create or update a group

POST /groups

curl https://api.userflow.com/groups \
  -XPOST \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
        "attributes": {
          "name": "Acme Inc.",
          "billing_plan": "plus",
          "signed_up_at": "2022-09-29T12:34:56.000+00:00"
        }
      }'

Response:

{
  "id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
  "object": "group",
  "attributes": {
    "name": "Acme Inc.",
    "billing_plan": "plus",
    "signed_up_at": "2022-09-29T12:34:56.000+00:00"
  },
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "memberships": null,
  "users": null
}

Creating and updating a group is the same operation. If a group with the same ID doesn't already exist in Userflow, it will be created. If it already exists, the given attributes will be merged into the existing group's attributes.

Note that groups can also be created/updated by embedding groups or memberships when creating or updating a user.

Request body

Key Description
id* String - Unique identifier for the group. Should match the ID the group has in your database.
attributes Object - A map with attributes to update for the group. You can add any attributes you want here. Existing attributes not included in the request will not be touched. Attribute values can either be supplied as literal values (string, boolean, number) or as operations (set, set_once, add, subtract). See Attributes.

* Required

Response body

Returns the created/updated group object.

Get a group

GET /groups/:group_id

curl https://api.userflow.com/groups/ab82c312-b3a4-4feb-870c-53dd336f955e \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
  "object": "group",
  "attributes": {
    "name": "Acme Inc.",
    "billing_plan": "plus",
    "signed_up_at": "2022-09-29T12:34:56.000+00:00"
  },
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "memberships": null,
  "users": null
}

Retrieves a group including all its attributes.

URL arguments

Key Description
group_id* String - Unique identifier for the group. Should match the ID the group has in your database.

* Required

Response body

Returns the group object, if found. Responds with 404 Not Found if not.

Delete a group

DELETE /groups/:group_id

curl https://api.userflow.com/groups/ab82c312-b3a4-4feb-870c-53dd336f955e \
  -XDELETE \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
  "object": "group",
  "deleted": true
}

Permanently deletes a group including all their attributes, memberships and events. It cannot be undone. Users who were members of the group will be left intact.

URL arguments

Key Description
group_id* String - Unique identifier for the group. Should match the ID the group has in your database.

* Required

Response body

Returns an object with a deleted key on success (or if the group already didn't exist - this call is idempotent).

List groups

GET /groups

curl https://api.userflow.com/groups \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "1",
      "object": "group",
      "attributes": {
        "name": "Acme Inc.",
        "signed_up_at": "2022-09-29T12:34:56.000+00:00"
      },
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "memberships": null,
      "users": null
    },
    ...
    {
      "id": "10",
      "object": "group",
      ...
    }
  ],
  "has_more": true,
  "url": "/groups",
  "next_page_url": "/groups?starting_after=10"
}

Returns a list of groups.

Query parameters

Key Description
condition String - Only include groups that match the given condition. Can be used to filter any attribute. See Condition filtering.
email String - Only include users whose email attribute equals this value.
limit Number - See Pagination.
order_by String or array of strings - See Ordering. Fields that groups can be ordered by: created_at (note that this is when the group was created with Userflow), attributes.name. Defaults to created_at.
segment_id String - Only include groups in this segment. Find a segment's ID in the Userflow UI via the three-dot menu at the top right of the segment page.
starting_after String - See Pagination.
user_id String - Only include groups that this user is a member of.

Response body

Returns a list object with a data property that contains an array of group objects.

Group memberships

Group memberships represent the many-to-many association between users and groups. One user can be a member of multiple groups. One group can have multiple users as members.

Memberships can hold attributes related to the user's role in a specific group. One user may be the Owner of one group and an Admin of another group. One group can have both an Owner and an Admin. You can't put the user's role in either the user's attributes or the group's attributes. Instead, you put it in the membership's attributes.

The group membership object

Example group membership object:

{
  "id": "e9b32bd0-63cb-415e-9c4f-477c85b92f97",
  "object": "group_membership",
  "attributes": {
    "role": "admin"
  },
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "group": null,
  "group_id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
  "user": null,
  "user_id": "2a845972-4cde-4cb4-ba14-5cb2fc15ec4c"
}
Key Description
id String - Unique identifier for the membership set by Userflow.
object String - Represents the object's type. Always group_membership.
attributes Object - A map with all the membership's attributes. You can add any attributes you want here.
created_at String - ISO 8601 date time representing when the membership was created with Userflow. Note that this is not the time the member user signed up in your app - use an attribute such as signed_up_at in the user's attributes object for that.
group Group object - Defaults to null, but can be expanded using ?expand=group.
group_id String - The ID of group.
user User object - Defaults to null, but can be expanded using ?expand=user.
user_id String - The ID of user.

Create or update a group membership

Memberships can't be created/updated directly. Instead, create or update a user and embed a list of group membership objects in the memberships field.

Remove a user from a group

DELETE /group_memberships?user_id=:user_id&group_id=:group_id

curl https://api.userflow.com/group_memberships?user_id=123&group_id=456 \
  -XDELETE \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "e9b32bd0-63cb-415e-9c4f-477c85b92f97",
  "object": "group_membership",
  "deleted": true
}

Removes a single user from a single group. Leaves the user and the group themselves intact.

URL arguments

Key Description
group_id* String - Unique identifier for the group. Should match the ID the group has in your database.
user_id* String - Unique identifier for the user. Should match the ID the user has in your database.

* Required

Response body

Returns an object with a deleted key on success (or if the user already wasn't a member of the group - this call is idempotent).

List group memberships

Memberships can't be listed directly. Instead, retrieve a user or a group and use the ?expand query parameter to expand the user/group's memberships.

Events

You can track events for users and groups for analytics purposes or to segment and personalize your flows.

Events can be either associated with just a user, just a group, or both a user and a group.

The event object

Example event object:

{
  "id": "4738382",
  "object": "event",
  "attributes": {
    "plan_name": "plus",
    "plan_price": 199
  },
  "created_at": "2022-11-29T12:34:56.000+00:00",
  "group": null,
  "group_id": "ab82c312-b3a4-4feb-870c-53dd336f955e",
  "name": "subscription_activated",
  "time": "2022-11-29T12:34:56.000+00:00",
  "user": null,
  "user_id": "c2dfad13-ee7d-4fb2-9b4f-d450716b4791"
}
Key Description
id String - Unique identifier for the event.
object String - Represents the object's type. Always event.
attributes Object - A map of attributes associated with the event. You can add any attributes you want here.
created_at String - ISO 8601 date time representing when the event was created with Userflow.
group Group object - Defaults to null, but can be expanded using ?expand=group.
group_id String - ID of the group the event is associated with (if any). Should match the ID the group has in your database.
name String - Event name (the action the user performed).
time String - ISO 8601 date time representing when the event actually happened. This may differ from created_at if your integration explicitly sends time, or if you have imported historic events.
user User object - Defaults to null, but can be expanded using ?expand=user.
user_id String - ID of the user the event is associated with (if any). Should match the ID the user has in your database.

Track an event

POST /events

curl https://api.userflow.com/events \
  -XPOST \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "user_id": "c2dfad13-ee7d-4fb2-9b4f-d450716b4791",
        "name": "subscription_activated",
        "attributes": {
          "plan_name": "plus",
          "plan_price": 199
        }
      }'

Response:

{
  "id": "4738382",
  "object": "event",
  "attributes": {
    "plan_name": "plus",
    "plan_price": 199
  },
  "created_at": "2022-11-29T12:34:56.000+00:00",
  "group_id": null,
  "group": null,
  "name": "subscription_activated",
  "time": "2022-11-29T12:34:56.000+00:00",
  "user_id": "c2dfad13-ee7d-4fb2-9b4f-d450716b4791",
  "user": null
}

Stores an event associated with a user and/or a group.

Request body

Key Description
attributes Object - A map of attributes associated with the event. You can add any attributes you want here. Attribute names (the keys) must conform to our Attribute naming rules.
group_id* String - ID of the group the event is associated with (if any). Should match the ID the group has in your database.
name** String - Name of the action a user has performed. Must conform to our Event naming rules.
time String - ISO 8601 date time representing when the event actually happened. If not set, it'll default to the curren time when the event is received.
user_id* String - ID of the user the event is associated with (if any). Should match the ID the user has in your database.

* One (or both) of user_id and group_id is required.
** Required

Response body

Returns the created event object.

Content

Content is a common term for flows, checklists and launchers.

Note that content is versioned. The actual contents of content objects are found in content versions.

The content object

Example content object:

{
  "id": "54bce034-1303-4200-a09a-780a2eee355d",
  "object": "content",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "draft_version_id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
  "draft_version": null,
  "name": "Example flow",
  "published_version_id": "c8eeb97b-43aa-47d9-bed2-a3d964900977",
  "published_version": null,
  "type": "flow"
}
Key Description
id String - Unique identifier for the content object.
object String - Represents the object's type. Always content.
created_at String - ISO 8601 date time representing when the content object was first created.
draft_version_id String - The ID of the version currently in the Builder.
draft_version Content version object - Defaults to null, but can be expanded using ?expand=draft_version.
name String - Name given in the Builder.
published_version_id String - The ID of the version currently published in the environment.
published_version Content version object - Defaults to null, but can be expanded using ?expand=published_version.
type String - Indicates what type of content it is. One of: checklist, flow, launcher

Get a content object

GET /content/:content_id

curl https://api.userflow.com/content/54bce034-1303-4200-a09a-780a2eee355d \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "54bce034-1303-4200-a09a-780a2eee355d",
  "object": "content",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "draft_version_id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
  "draft_version": null,
  "name": "Example flow",
  "published_version_id": "c8eeb97b-43aa-47d9-bed2-a3d964900977",
  "published_version": null,
  "type": "flow"
}

Retrieves a single content object by its ID.

URL arguments

Key Description
content_id* String - Unique identifier for the content object.

* Required

Response body

Returns the content object, if found. Responds with 404 Not Found if not.

List content

GET /content

curl https://api.userflow.com/content \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "54bce034-1303-4200-a09a-780a2eee355d",
      "object": "content",
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "draft_version_id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
      "draft_version": null,
      "name": "Example flow",
      "published_version_id": "c8eeb97b-43aa-47d9-bed2-a3d964900977",
      "published_version": null,
      "type": "flow"
    },
    ...
    {
      "id": "f7ddbadb-4368-4367-b728-b69610450cbd",
      "object": "content",
      ...
    }
  ],
  "has_more": true,
  "url": "/content",
  "next_page_url": "/content?starting_after=f7ddbadb-4368-4367-b728-b69610450cbd"
}

Filter by type:

curl https://api.userflow.com/content?type=checklist \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Returns a list of your content.

Query parameters

Key Description
limit Number - See Pagination.
order_by String or array of strings - See Ordering. Fields that content objects can be ordered by: created_at, name. Defaults to created_at.
starting_after String - See Pagination.
type String - Only include content objects of the given type. Supported values: checklist, flow, launcher.

Response body

Returns a list object with a data property that contains an array of content objects.

Content versions

Content (flows, checklists and launchers) is versioned. A new version is automatically created by Userflow when new edits are made and when publishing content.

The content version object

Example content version object:

{
  "id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
  "object": "content_version",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "description": "Optional example description",
  "edited_at": "2022-06-29T23:45:67.000+00:00",
  "number": 3,
  "questions": null,
  "tasks": null
}
Key Description
id String - Unique identifier for the content object.
object String - Represents the object's type. Always content_version.
created_at String - ISO 8601 date time representing when the content object was first created.
description String - Optional user-provided description of the version.
edited_at String - ISO 8601 date time representing when the version was last edited (or published, in which case it was frozen at this moment).
number Number - The version number. Version numbers are incremental for each content object.
questions Array of question objects - A list of all questions in the version. Only flows can have questions. Defaults to null, but can be expanded using ?expand=questions.
tasks Array of checklist task objects - A list of all tasks in the version. Only checklists can have tasks. Defaults to null, but can be expanded using ?expand=tasks.

The question object

Example question object:

{
  "id": "d3af62f1-4de3-4c9a-b20c-85b85dce0fef",
  "object": "question",
  "cvid": "540649a1-9443-4b59-90a2-2262e58744f8",
  "name": "Example Question",
  "type": "text"
}
Key Description
id String - Unique identifier for the question. Important: The ID of questions change between versions. When a new version is created, all its components are copied from the old version, but with new id values. You can use cvid for an ID that's stable across versions.
object String - Represents the object's type. Always question.
cvid String - Short for Cross-Version ID. An ID that stays the same for the logically same question across versions. Use this if you ever have to refer to a question - not id, since it'll change between versions.
name String - Name given to the question in the Builder.
type String - Type of question. Supported values: multiline_text, multiple_choice, nps, stars, text.

The checklist task object

Example checklist task object:

{
  "id": "1a8152f8-41cf-4e10-9e74-71cafd6dc8b2",
  "object": "checklist_task",
  "cvid": "ad157908-b182-4585-b0ea-21fdcb2da684",
  "name": "Example Task"
}
Key Description
id String - Unique identifier for the task. Important: The ID of tasks change between versions. When a new version is created, all its components are copied from the old version, but with new id values. You can use cvid for an ID that's stable across versions.
object String - Represents the object's type. Always checklist_task.
cvid String - Short for Cross-Version ID. An ID that stays the same for the logically same task across versions. Use this if you ever have to refer to a task - not id, since it'll change between versions.
name String - Name given to the task in the Builder.

Get a content version

GET /content_versions/:version_id

curl https://api.userflow.com/content_versions/152a63cc-0a49-475a-a8e4-5f89bee94fe6 \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
  "object": "content_version",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "description": "Optional example description",
  "edited_at": "2022-06-29T23:45:67.000+00:00",
  "number": 3,
  "questions": null,
  "tasks": null
}

Retrieves a single content version by its ID.

URL arguments

Key Description
version_id* String - Unique identifier for the content version.

* Required

Response body

Returns the content version object, if found. Responds with 404 Not Found if not.

List content versions

GET /content_versions

curl https://api.userflow.com/content_versions?content_id=54bce034-1303-4200-a09a-780a2eee355d \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
      "object": "content_version",
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "description": "Optional example description",
      "edited_at": "2022-06-29T23:45:67.000+00:00",
      "number": 3,
      "questions": null,
      "tasks": null
    },
    ...
    {
      "id": "c8eeb97b-43aa-47d9-bed2-a3d964900977",
      "object": "content_version",
      ...
    }
  ],
  "has_more": true,
  "url": "/content_versions",
  "next_page_url": "/content_versions?starting_after=c8eeb97b-43aa-47d9-bed2-a3d964900977"
}

Filter by version number:

curl https://api.userflow.com/content_versions?content_id=54bce034-1303-4200-a09a-780a2eee355d&number=3 \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Returns a list of versions for a specific content object.

Query parameters

Key Description
content_id* String - List versions of this content object. Must be supplied - we currently don't support listing versions across content objects.
limit Number - See Pagination.
number Number - Filter by version number to find a specific version.
order_by String or array of strings - See Ordering. Fields that content objects can be ordered by: number. Defaults to number.
starting_after String - See Pagination.

* Required

Response body

Returns a list object with a data property that contains an array of content version objects.

Content sessions

A session is a specific user's journey through a specific content object (flow, checklist or launcher). It tracks their progress and records survey answers they provide.

The content session object

Example content session object:

{
  "id": "33af21fd-f025-43fc-a492-cf5179b38ee3",
  "object": "content_session",
  "answers": null,
  "completed_at": "2022-10-17T12:35:56.000+00:00",
  "completed": true,
  "content_id": "54bce034-1303-4200-a09a-780a2eee355d",
  "content": null,
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "group_id": null,
  "group": null,
  "is_preview": false,
  "last_activity_at": "2022-10-17T12:35:56.000+00:00",
  "launcher_activated": false,
  "progress": "1",
  "user_id": "34907ae0-24e0-4261-ac31-3c7299a354c0",
  "user": null,
  "version_id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
  "version": null
}
Key Description
id String - Unique identifier for the content session.
object String - Represents the object's type. Always content_session.
answers Array of content session answer objects - A list of all answers provided by the user to survey questions in the flow. Only flow sessions can have answers. Defaults to null, but can be expanded using ?expand=answers.
completed_at String - ISO 8601 date time representing when the flow/checklist was completed. null if not completed.
completed Boolean - Whether the flow/checklist has been completed. For flows, this means when the user visited a goal step. For checklists, this means that the user completed all tasks.
content_id String - The ID of the content object (i.e. flow/checklist/launcher) that this session is running.
content Content object - Defaults to null, but can be expanded using ?expand=content.
created_at String - ISO 8601 date time representing when the session was first created.
group_id String - The ID of a group, if this session was started under a group.
group Group object - Defaults to null, but can be expanded using ?expand=group.
is_preview Boolean - true if the session was started by a team member previewing a draft version.
last_activity_at String - ISO 8601 date time representing when the user last interacted with the content (e.g. went to the next step, expanded a checklist etc.).
launcher_activated Boolean - If this is a session launcher, will be true if the user has activated the launcher by e.g. hovering over or clicking the target element.
progress Decimal string - A decimal representation of the user's progress through the flow. 1 means that the flow is fully completed (100%). 0.4 means that the user is 40% through.
user_id String - The ID of the user seeing the content.
user User object - Defaults to null, but can be expanded using ?expand=user.
version_id String - The ID of the version that this session is running.
version Content object - Defaults to null, but can be expanded using ?expand=version.

The content session answer object

Example content session answer object:

{
  "id": "23dec03f-3de6-4e89-89e6-eed0785eecd0",
  "object": "content_session_answer",
  "answer_type": "text",
  "answer_value": "I really like your app!",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "question_cvid": "540649a1-9443-4b59-90a2-2262e58744f8",
  "question_name": "Feedback"
}
Key Description
id String - Unique identifier for the answer.
object String - Represents the object's type. Always content_session_answer.
answer_type String - Represents the data type of the answer. Supported values: number, text, list.
answer_value String - The answer given by the user. If answer_type is text, then answer_value is a string. If answer_type is number, then answer_value is a numeric string. If answer_type is list, answer_value is an array of strings.
created_at String - ISO 8601 date time representing when the answer was provided.
question_cvid String - The Cross-Version ID of the question that was answered (cvid of a question object).
question_name String - The name of the question that was answered (name of a question object).

Get a content session

GET /content_sessions/:session_id

curl https://api.userflow.com/content_sessions/33af21fd-f025-43fc-a492-cf5179b38ee3 \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "33af21fd-f025-43fc-a492-cf5179b38ee3",
  "object": "content_session",
  "answers": null,
  "completed_at": "2022-10-17T12:35:56.000+00:00",
  "completed": true,
  "content_id": "54bce034-1303-4200-a09a-780a2eee355d",
  "content": null,
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "group_id": null,
  "group": null,
  "is_preview": false,
  "last_activity_at": "2022-10-17T12:35:56.000+00:00",
  "launcher_activated": false,
  "progress": "1",
  "user_id": "34907ae0-24e0-4261-ac31-3c7299a354c0",
  "user": null,
  "version_id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
  "version": null
}

Retrieves a single content session by its ID.

URL arguments

Key Description
session_id* String - Unique identifier for the content session.

* Required

Response body

Returns the content session object, if found. Responds with 404 Not Found if not.

Delete a content session

DELETE /content_sesssions/:content_session_id

curl https://api.userflow.com/content_sessions/33af21fd-f025-43fc-a492-cf5179b38ee3 \
  -XDELETE \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "33af21fd-f025-43fc-a492-cf5179b38ee3",
  "object": "content_session",
  "deleted": true
}

Permanently deletes a content session by its id, including its associated progress and survey answers. It cannot be undone.

URL arguments

Key Description
content_session_id* String - Unique identifier for the content session.

* Required

Response body

Returns an object with a deleted key on success (or if the content session already didn't exist - this call is idempotent).

End a content session

POST /content_sessions/:content_session_id/end

curl https://api.userflow.com/content_sessions/33af21fd-f025-43fc-a492-cf5179b38ee3/end \
  -XPOST \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "33af21fd-f025-43fc-a492-cf5179b38ee3",
  "object": "content_session",
  "answers": null,
  "completed_at": "2022-10-17T12:35:56.000+00:00",
  "completed": true,
  "content_id": "54bce034-1303-4200-a09a-780a2eee355d",
  "content": null,
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "group_id": null,
  "group": null,
  "is_preview": false,
  "last_activity_at": "2022-10-17T12:55:56.000+00:00",
  "launcher_activated": false,
  "progress": "1",
  "user_id": "34907ae0-24e0-4261-ac31-3c7299a354c0",
  "user": null,
  "version_id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
  "version": null
}

Ends an existing content session that is currently active for a user. This call is idempotent.

URL arguments

Key Description
content_session_id* String - ID of the content session to update.

* Required

Response body

Returns the updated content session object.

List content sessions

GET /content_sessions

curl https://api.userflow.com/content_sessions?content_id=54bce034-1303-4200-a09a-780a2eee355d \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "33af21fd-f025-43fc-a492-cf5179b38ee3",
      "object": "content_session",
      "answers": null,
      "completed_at": "2022-10-17T12:35:56.000+00:00",
      "completed": true,
      "content_id": "54bce034-1303-4200-a09a-780a2eee355d",
      "content": null,
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "group_id": null,
      "group": null,
      "is_preview": false,
      "last_activity_at": "2022-10-17T12:35:56.000+00:00",
      "launcher_activated": false,
      "progress": "1",
      "user_id": "34907ae0-24e0-4261-ac31-3c7299a354c0",
      "user": null,
      "version_id": "152a63cc-0a49-475a-a8e4-5f89bee94fe6",
      "version": null
    },
    ...
    {
      "id": "0a2b7674-ed69-4261-9389-ce68e32077d2",
      "object": "content_session",
      ...
    }
  ],
  "has_more": true,
  "url": "/content_sessions",
  "next_page_url": "/content_sessions?starting_after=0a2b7674-ed69-4261-9389-ce68e32077d2"
}

Check if a specific user has seen a specific flow:

curl https://api.userflow.com/content_sessions?user_id=34907ae0-24e0-4261-ac31-3c7299a354c0&content_id=54bce034-1303-4200-a09a-780a2eee355d&number=3 \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Returns a list of content sessions.

Query parameters

Key Description
content_id String - Only include sessions for this content object.
limit Number - See Pagination.
order_by String or array of strings - See Ordering. Fields that content objects can be ordered by: created_at, last_activity_at. Defaults to created_at.
starting_after String - See Pagination.
user_id String - Only include sessions for this user.

Response body

Returns a list object with a data property that contains an array of content session objects.

Attribute definitions

You can associate any attributes with users/events that you want. Userflow keeps track of all the attributes you use through attribute definitions.

Attribute definitions are automatically created when you send new attributes not seen by Userflow before. It's currently not possible to create/update/delete attribute definitions through this API.

The attribute definition object

Example attribute definition object:

{
  "id": "dfb0c3ed-a8d1-4e73-8b95-b307fa322c3a",
  "object": "attribute_definition",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "data_type": "datetime",
  "description": "When user first signed up in your app",
  "display_name": "Signed Up",
  "name": "signed_up_at",
  "scope": "user"
}
Key Description
id String - Unique identifier for the attribute definition.
object String - Represents the object's type. Always attribute_definition.
created_at String - ISO 8601 date time representing when attribute definition was first created.
data_type String - Indicates what type of values can be used for the attributes. See Attribute data types for possible values for custom attributes. Userflow-internal attributes can use the following data types (these attributes are not writable through the API): checklist_task, content, content_session, content_version, flow_step, random_ab, random_number.
description String - A human-readable description of what the attribute is used for. Can be changed in the Userflow Dashboard.
display_name String - A human-readable name for the attribute. Used in most Userflow UI when referring to attributes. Can be changed in the Userflow Dashboard.
name String - The key used when setting the attribute value through the attributes object in e.g. Create or update a user. Cannot be changed once an attribute definition is created.
scope String - Defines which objects this attribute can be associated with. Supported values: event, group, group_membership, user.

List attribute definitions

GET /attribute_definitions

curl https://api.userflow.com/attribute_definitions \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "dfb0c3ed-a8d1-4e73-8b95-b307fa322c3a",
      "object": "attribute_definition",
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "data_type": "datetime",
      "description": "When user first signed up in your app",
      "display_name": "Signed Up",
      "name": "signed_up_at",
      "scope": "user"
    },
    ...
    {
      "id": "f6293417-f262-4ff6-88b7-3afce56a0de1",
      "object": "attribute_definition",
      ...
    }
  ],
  "has_more": true,
  "url": "/attribute_definitions",
  "next_page_url": "/attribute_definitions?starting_after=f6293417-f262-4ff6-88b7-3afce56a0de1"
}

Filter by scope:

curl https://api.userflow.com/attribute_definitions?scope=user \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Filter by event names:

curl https://api.userflow.com/attribute_definitions?event_name[]=flow_started&event_name[]=flow_ended \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Returns a list of your attribute definitions.

Query parameters

Key Description
event_name String or array of strings - Only include attribute definitions used in the given event(s).
limit Number - See Pagination.
order_by String or array of strings - See Ordering. Fields that attribute definitions can be ordered by: created_at, display_name, name. Defaults to display_name.
scope String - Only include attribute definitions with the given scope. Supported values: event, group, group_membership, user.
starting_after String - See Pagination.

Response body

Returns a list object with a data property that contains an array of attribute definition objects.

Event definitions

You can track any custom event with Userflow that you want. Userflow keeps track of all the events you use through event definitions.

Event definitions are automatically created when you track new events not seen by Userflow before. It's currently not possible to create/update/delete attribute definitions through this API.

For tracking events, see Events.

The event definition object

Example event definition object:

{
  "id": "9cf1de05-9a3a-4719-b8a9-75316a5f86ce",
  "object": "event_definition",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "description": "A Userflow flow was started",
  "display_name": "Flow Started",
  "name": "flow_started"
}
Key Description
id String - Unique identifier for the event definition.
object String - Represents the object's type. Always event_definition.
created_at String - ISO 8601 date time representing when event definition was first created.
description String - A human-readable description of what the event means. Can be changed in the Userflow Dashboard.
display_name String - A human-readable name for the event. Used in most Userflow UI when referring to events. Can be changed in the Userflow Dashboard.
name String - Used as the name field in event objects. Cannot be changed once an event definition is created.

Event naming

Event names must consist only of a-z, A-Z, 0-9, underscores, dashes, periods(.) and spaces. We recommend using snake_case everywhere though. Events' human-friendly display names (e.g. "Subscription Activated" for subscription_activated) can also be configured in the Userflow UI.

We recommend using event names consisting of a noun and a past-tense verb. Check out this great event naming guide by Segment.

List event definitions

GET /event_definitions

curl https://api.userflow.com/event_definitions \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "9cf1de05-9a3a-4719-b8a9-75316a5f86ce",
      "object": "event_definition",
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "description": "A Userflow flow was started",
      "display_name": "Flow Started",
      "name": "flow_started"
    },
    ...
    {
      "id": "1821de25-316f-4b9a-b0e6-52e45f62a012",
      "object": "event_definition",
      ...
    }
  ],
  "has_more": true,
  "url": "/event_definitions",
  "next_page_url": "/event_definitions?starting_after=1821de25-316f-4b9a-b0e6-52e45f62a012"
}

Returns a list of your event definitions.

Query parameters

Key Description
limit Number - See Pagination.
order_by String or array of strings - See Ordering. Fields that event definitions can be ordered by: created_at, display_name, name. Defaults to display_name.
starting_after String - See Pagination.

Response body

Returns a list object with a data property that contains an array of event definition objects.

Webhook subscriptions

You can create webhook subscriptions to be notified when certain events happen in your Userflow account.

When e.g. a user is created or a user event is tracked, Userflow will send a POST request to a URL of your choosing. Often this URL would hit your own servers, so your back-end can react to the webhook.

The webhook subscription object

Example webhook subscription object:

{
  "id": "c8783643-7f16-4a02-b736-4f5a327f8231",
  "object": "webhook_subscription",
  "api_version": "2020-01-03",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "disabled": false,
  "topics": ["user", "event"],
  "url": "https://example.com/hooks/userflow"
}

You create webhook subscription objects to subscribe to notifications.

Key Description
id String - Unique identifier for the webhook subscription.
object String - Represents the object's type. Always webhook_subscription.
api_version String - The API version notifications will be sent to url with. See Versioning.
created_at String - ISO 8601 date time representing when the webhook subscription was created.
disabled boolean - Used to temporarily make Userflow stop sending notifications (when set to true).
secret String - The subscription's secret, used to generate webhook signatures. Only returned at creation (POST /webhook_subscriptions).
topics Array of strings - The webhook topics your subscription will be notified of.
url String - The URL Userflow should send POST requests to when there are new notifications.

The webhook notification object

Example webhook notification object for when an event is tracked:

{
  "id": "ec3a21f3-88fe-43e8-8711-cfc803aaad54",
  "object": "webhook_notification",
  "api_version": "2020-01-03",
  "created_at": "2022-11-29T12:34:56.000+00:00",
  "data": {
    "object": {
      "id": "4738382",
      "object": "event",
      "attributes": {
        "plan_name": "plus",
        "plan_price": 199
      },
      "created_at": "2022-11-29T12:34:56.000+00:00",
      "name": "subscription_activated",
      "time": "2022-11-29T12:34:56.000+00:00",
      "user_id": "c2dfad13-ee7d-4fb2-9b4f-d450716b4791"
    }
  },
  "topic": "event.tracked.subscription_activated"
}

Example webhook notification object for when a user is updated:

{
  "id": "c30d70fd-eb53-4132-bc99-90792677497b",
  "object": "webhook_notification",
  "api_version": "2020-01-03",
  "created_at": "2022-11-29T12:34:56.000+00:00",
  "data": {
    "object": {
      "id": "4738382",
      "object": "user",
      "attributes": {
        "email": "annabelle@example.com",
        "email_verified": true,
        "name": "Annabelle Terry",
        "project_count": 17,
        "signed_up_at": "2022-09-29T12:34:56.000+00:00"
      },
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "groups": null,
      "memberships": null
    },
    "previous_attributes": {
      "email_verified": false,
      "project_count": 14
    },
    "updated_attributes": {
      "email_verified": true,
      "project_count": 17
    }
  },
  "topic": "user.updated"
}

Userflow creates webhook notifications when certain things happen in your account (e.g. a user is created), and sends them to your webhook subscriptions.

Key Description
id String - Unique identifier for the webhook notification.
object String - Represents the object's type. Always webhook_notification.
api_version String - The API version of the webhook subscription this notification was sent for. See Versioning.
created_at String - ISO 8601 date time representing when the webhook notification was created.
data Object - Holds details about the notification.
data.object Object - The object of the notification. E.g. for the user.created topic, data.object will be a user object, and for the event.tracked.<name> topic it'll be an event object.
data.previous_attributes Object - Only set in <object>.updated topic notifications. Contains the old values of all changed attributes as they were before the change that caused this notification.
data.updated_attributes Object - Only set in <object>.updated topic notifications. Contains the new values of all changed attributes.
topic String - The notification's webhook topic.

Webhook topics

Webhook subscriptions only receive notifications for topics they subscribe to.

Topics are namespaced. If you subscribe to e.g. user, you get'll all user-related notifications such as user.created and user.updated.

You can also use * to subscribe to all topics.

Topic Description
* Matches ALL topics.
event Matches all event-related topics.
event.tracked When any event is tracked.
event.tracked.<name> When a <name> event is tracked. You can use any of name value from your event definitions. Example: event.tracked.flow_started
group Matches all group-related topics.
group.created When a new group is created with Userflow.
group.updated When an existing group is updated.
user Matches all user-related topics.
user.created When a new user is created with Userflow.
user.updated When an existing user is updated.

Receiving webhooks

Example webhook notification request headers (what Userflow sends to your server):

POST /hooks/userflow
Host: your.app.com
Content-Type: application/json; charset=utf-8
User-Agent: Userflow/1.0 (https://userflow.com/docs/webhooks)
Userflow-Signature: t=1578424823,v1=5cb0c1733224ad819f93dcf4b902cd714c83afd4e9e95412f4bc6cd1b94a3aac

Example webhook notification request body (what Userflow sends to your server):

{
  "id": "ec3a21f3-88fe-43e8-8711-cfc803aaad54",
  "object": "webhook_notification",
  "api_version": "2020-01-03",
  "created_at": "2022-11-29T12:34:56.000+00:00",
  "data": {
    "object": {
      "id": "4738382",
      "object": "event",
      "attributes": {
        "plan_name": "plus",
        "plan_price": 199
      },
      "created_at": "2022-11-29T12:34:56.000+00:00",
      "name": "subscription_activated",
      "time": "2022-11-29T12:34:56.000+00:00",
      "user_id": "c2dfad13-ee7d-4fb2-9b4f-d450716b4791"
    }
  },
  "topic": "event.tracked.subscription_activated"
}

Userflow will send a POST request to your webhook subscription's url every time there's a new notification that matches your subscription's topics.

The request body is a webhook notification object.

We strongly recommend that you verify the signature header to avoid fake requests from third parties.

If you handle the webhook successfully, you must return a 2xx (e.g. 200) status code to tell Userflow that everything is in order. If Userflow does not receive a 2xx response status code within a timeout of 15 seconds, Userflow will retry the request using exponential backoff. Userflow will give up after 3 days.

If your server has to perform time-consuming operations, we recommend that you enqueue it to run in the background, and acknowledge the webhook with a 200 OK response immediately. This is to avoid the webhook request from timing out and then being re-delivered unnecessarily again later.

Even though webhooks are sent almost immediately by Userflow, we do not guarantee the order of events. A new user may be created, then shortly after be updated. Your app should expect possibly receiving the user.updated notification for that user before the user.created notification.

Webhook signatures

Example Userflow-Signature header:

Userflow-Signature: t=1578424823,v1=5cb0c1733224ad819f93dcf4b902cd714c83afd4e9e95412f4bc6cd1b94a3aac

Userflow includes a Userflow-Signature header to let you verify that webhook notifications are in fact coming from Userflow and not a third party.

The Userflow-Signature header also includes a timestamp, which mitigates replay attacks, where an attacker intercepts a valid payload and signature, and then re-transmits it later. This timestamp is also part of the signed payload, which means an attacker can't change the timestamp without also invalidating the signature. If a timestamp is too old (e.g. > 5 minutes), we recommend that you ignore the request. We recommend using Network Time Protocol (NTP) to ensure that your server’s clock is accurate (Userflow uses NTP, too).

You need the secret returned when creating your webhook subscription to verify signatures. Secrets belong to subscriptions, meaning if your account has multiple subscriptions then they'll each have their own unique secret.

The Userflow-Signature header is on the form t=<timestamp>,v1=<signature>. <timestamp> is a UNIX timestamp (in seconds) representing when the signature was generated. Userflow generates a new signature every time we attempt to deliver a notification (including on retries). <signature> is an HMAC signature. We use v1 as key for the signature to able to support new signature schemes in the future. See example on the right.

Verifying signatures

Example JavaScript implementation:

// This function will throw if the signature is invalid
function verifyUserflowSignature(body, header, secret) {
  // Make sure body is a string (not a buffer)
  body = Buffer.isBuffer(body) ? body.toString('utf8') : body

  // Reject if timestamp is more than 5 minutes ago
  let tolerance = 300 // seconds

  // Extract header information
  let t
  let signature
  header.split(',').forEach(part => {
    let [k, v] = part.split('=')
    if (k === 't') {
      t = v
    } else if (k === 'v1') {
      signature = v
    }
  })
  if (!t) {
    throw new Error('Userflow-Signature header check failed: Missing t')
  }
  if (!signature) {
    throw new Error(
      'Userflow-Signature header check failed: Missing v1 signature'
    )
  }

  // Verify timestamp age
  let timestampAge = Math.floor(Date.now() / 1000) - t
  if (timestampAge > tolerance) {
    throw new Error(
      `Userflow-Signature header check failed: Timestamp is more than ${tolerance} seconds ago`
    )
  }

  // Compare signatures
  let signedPayload = t + '.' + body
  let expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload, 'utf8')
    .digest('hex')
  if (signature !== expectedSignature) {
    throw new Error(
      'Userflow-Signature header check failed: Incorrect signature'
    )
  }

  // Signature is OK!
}

Example of using verifyUserflowSignature in Express.js handler:

const secret = 'whsec_...'

// Make sure the handler gets the body as a raw string
const parser = bodyParser.raw({type: 'application/json'})

app.post('/hooks/userflow', parser, (request, response) => {
  // Verify signature
  try {
    verifyUserflowSignature(
      request.body,
      request.headers['userflow-signature'],
      secret
    )
  } catch (e) {
    return response.status(400).json({error: e.message})
  }

  // TODO: Do your thing with the notification
  let notification = JSON.parse(request.body)

  // Reply 200 OK to tell Userflow everything is good
  return response.json({ok: true})
})

Here's how to verify that the Userflow-Signature header is valid:

  1. Obtain the header value, the raw request body (as a raw string, not a parsed JSON object), and your webhook subscription secret.
  2. Extract the timestamp and signature from the header: Split the header by ,, then split each pair by =, then get the timestamp from the pair with key t and the signature from the pair with key v1.
  3. If the timestamp is more than e.g. 300 seconds (5 minutes) in the past, then reject the request.
  4. Build the signed payload by concatenating the timestamp, a period (.), and the raw request body (as a string).
  5. Calculate the expected signature by computing an HMAC with the SHA256 hash function, using the webhook subscription secret as the key, and the signed payload (from step 4) as the message.
  6. Compare the actual signature (from step 2) with the expected signature (from step 5). Reject the request if they don't match.

Create a webhook subscription

POST /webhook_subscriptions

curl https://api.userflow.com/webhook_subscriptions \
  -XPOST \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "api_version": "2020-01-03",
        "url": "https://example.com/hooks/userflow",
        "topics": ["user", "event"]
      }'

Response:

{
  "id": "c8783643-7f16-4a02-b736-4f5a327f8231",
  "object": "webhook_subscription",
  "api_version": "2020-01-03",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "disabled": false,
  "secret": "whsec_56nfnf5isvf5pldjcyesd4rxeq",
  "topics": ["user", "event"],
  "url": "https://example.com/hooks/userflow"
}

Creates a new webhook subscription.

Notifications will immediately begin sending to url for all events occurring going forward. See Receiving webhooks.

Request body

Key Description
api_version String - The API version notifications will be sent to url with. See Versioning.
topics Array of strings* - The webhook topics your subscription will be notified of.
url String* - The URL Userflow should send POST requests to when there are new notifications.

* Required

Response body

Returns the created webhook subscription object.

Get a webhook subscription

GET /webhook_subscriptions/:webhook_subscription_id

curl https://api.userflow.com/webhook_subscriptions/72a63cf6-91bf-4260-8258-a45db633d611 \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "c8783643-7f16-4a02-b736-4f5a327f8231",
  "object": "webhook_subscription",
  "api_version": "2020-01-03",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "disabled": false,
  "topics": ["user", "event"],
  "url": "https://example.com/hooks/userflow"
}

Retrieves a webhook subscription.

URL arguments

Key Description
webhook_subscription_id* String - ID of the webhook subscription to retrieve.

* Required

Response body

Returns the webhook subscription object, if found. Responds with 404 Not Found if not.

Update a webhook subscription

PATCH /webhook_subscriptions/:webhook_subscription_id

curl https://api.userflow.com/webhook_subscriptions \
  -XPATCH \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "disabled": true
      }'

Response:

{
  "id": "c8783643-7f16-4a02-b736-4f5a327f8231",
  "object": "webhook_subscription",
  "api_version": "2020-01-03",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "disabled": true,
  "secret": "whsec_56nfnf5isvf5pldjcyesd4rxeq",
  "topics": ["user", "event"],
  "url": "https://example.com/hooks/userflow"
}

Updates an existing webhook subscription.

URL arguments

Key Description
webhook_subscription_id* String - ID of the webhook subscription to update.

* Required

Request body

Key Description
api_version String - The API version notifications will be sent to url with. See Versioning.
topics Array of strings - The webhook topics your subscription will be notified of.
url String - The URL Userflow should send POST requests to when there are new notifications.
disabled boolean - Used to temporarily make Userflow stop sending notifications. If you set this to true, notifications will immediately stop being sent.

Response body

Returns the updated webhook subscription object.

Delete a webhook subscription

DELETE /webhook_subscriptions/:webhook_subscription_id

curl https://api.userflow.com/webhook_subscriptions/c8783643-7f16-4a02-b736-4f5a327f8231 \
  -XDELETE \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "c8783643-7f16-4a02-b736-4f5a327f8231",
  "object": "webhook_subscription",
  "deleted": true
}

Permanently deletes a webhook subscription. It cannot be undone.

Webhook notifications will immediately stop being sent to url.

URL arguments

Key Description
webhook_subscription_id* String - ID of the webhook subscription to delete.

* Required

Response body

Returns an object with a deleted key on success (or if the webhook subscription already didn't exist - this call is idempotent).

List webhook subscriptions

GET /webhook_subscriptions

curl https://api.userflow.com/webhook_subscriptions \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "c8783643-7f16-4a02-b736-4f5a327f8231",
      "object": "webhook_subscription",
      "api_version": "2020-01-03",
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "disabled": false,
      "topics": ["user", "event"],
      "url": "https://example.com/hooks/userflow"
    },
    ...
    {
      "id": "0a55b4be-b327-4a2b-a66f-16d1f7bf6519",
      "object": "webhook_subscription",
      ...
    }
  ],
  "has_more": true,
  "url": "/webhook_subscriptions",
  "next_page_url": "/webhook_subscriptions?starting_after=0a55b4be-b327-4a2b-a66f-16d1f7bf6519"
}

Returns a list of your webhook subscriptions.

Query parameters

Key Description
limit Number - See Pagination.
order_by String or array of strings - See Ordering. Fields that webhook subscriptions can be ordered by: created_at, url. Defaults to created_at.
starting_after String - See Pagination.

Response body

Returns a list object with a data property that contains an array of webhook subscription objects.

Accounts

An account represents a Userflow account with associated environments, members, and settings.

The account object

Example account object:

{
  "id": "3f2a4b56-7c8d-9e0f-1a2b-3c4d5e6f7a8b",
  "object": "account",
  "name": "Acme Inc.",
  "slug": "acme-inc",
  "created_at": "2023-01-01T00:00:00.000Z",
  "is_2fa_enforced": true,
  "environments": null
}

Example with expanded environments

{
  "id": "3f2a4b56-7c8d-9e0f-1a2b-3c4d5e6f7a8b",
  "object": "account",
  "name": "Acme Inc.",
  "slug": "acme-inc",
  "created_at": "2023-01-01T00:00:00.000Z",
  "is_2fa_enforced": true,
  "environments": [
    {
      "object": "environment",
      "id": "9a3c5e3e-7c4f-4a83-88be-86a3c85fa0e4",
      "name": "Production",
      "slug": "production",
      "created_at": "2023-01-01T00:00:01.000+00:00",
      "is_primary": true,
      "is_deactivated": false,
      "region": "US"
    }
  ]
}
Key Description
id String - Unique identifier for the account.
object String - Represents the object's type. Always account.
name String - Name of the account.
slug String - Unique slug for the account.
created_at String - ISO 8601 datetime representing when the account was created.
is_2fa_enforced Boolean - Represents whether two-factor authentication is enforced for the account.
environments Array of environment objects - List of environments associated with the account. Defaults to null, but can be expanded using ?expand=environments.

Get an account

GET /accounts/:account_id

curl https://api.userflow.com/accounts/3f2a4b56-7c8d-9e0f-1a2b-3c4d5e6f7a8b \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "3f2a4b56-7c8d-9e0f-1a2b-3c4d5e6f7a8b",
  "object": "account",
  "name": "Acme Inc.",
  "slug": "acme-inc",
  "created_at": "2023-01-01T00:00:00.000Z",
  "is_2fa_enforced": true,
  "environments": null
}

Retrieves an account by its ID.

URL arguments

Key Description
account_id* String - Unique identifier for the account.

* Required

Response body

Returns the account object, if found. Responds with 404 Not Found if not.

List accounts

GET /accounts

curl https://api.userflow.com/accounts \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "3f2a4b56-7c8d-9e0f-1a2b-3c4d5e6f7a8b",
      "object": "account",
      "name": "Acme Inc.",
      "slug": "acme-inc",
      "created_at": "2023-01-01T00:00:00.000Z",
      "is_2fa_enforced": true,
      "environments": null
    },
    ...
    {
      "id": "61c5e210-71c2-47e6-4354-6bcecda5c8fd",
      "object": "account",
      ...
    }
  ],
  "has_more": false,
  "url": "/accounts",
  "next_page_url": "/accounts?starting_after=61c5e210-71c2-47e6-4354-6bcecda5c8fd"
}

Returns a list of accounts that you are a member of.

Query parameters

Key Description
limit Number - See Pagination.
starting_after String - See Pagination.
order_by String or array of strings - See Ordering. Fields: name, created_at. Defaults to created_at.

Response body

Returns a list object with a data property that contains an array of account objects.

Environments

An environment isolates data within a Userflow account (e.g., production vs. staging). Environments for an account can be listed by using the expand query parameter, ?expand=environments in Get account and List accounts endpoints.

The environment object

Example environment object:

{
  "id": "a1b2c3d4-e5f6-7g8h-9i0j-k1l2m3n4o5p6",
  "object": "environment",
  "name": "Production",
  "slug": "production",
  "created_at": "2023-01-01T00:00:00.000Z",
  "is_primary": true,
  "is_deactivated": false,
  "region": "US"
}
Key Description
id String - Unique identifier for the environment.
object String - Represents the object's type. Always environment.
name String - Name of the environment.
slug String - Unique slug for the environment.
created_at String - ISO 8601 date time representing when the environment was created.
is_primary Boolean - Represents whether this is the primary environment.
is_deactivated Boolean - Represents whether the environment is deactivated.
region String - Region of the environment ("US" or "EU").

Members

A member represents an active team member in a Userflow account with access to team resources. A member can have roles and permissions which are specific to a particular account.

The member object

Example member object:

{
  "id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
  "object": "member",
  "name": "John Doe",
  "email": "john@example.com",
  "created_at": "2023-11-19T11:34:48.000+00:00",
  "avatar_url": "https://storage.googleapis.com/avatar.jpg",
  "is_2fa_enabled": true,
  "has_sso": true,
  "has_password": false,
  "role": "editor",
  "permissions": [
    {
      "action": "publish_flow",
      "subject": "environment",
      "subject_id": "9a3c5e3e-7c4f-4a83-88be-86a3c85fa0e4"
    }
  ]
}
Key Description
id String - Unique identifier for the member.
object String - Represents the object's type. Always member.
name String - Name of the member.
email String - Email address of the member.
created_at String - ISO 8601 datetime representing when the member joined the account.
avatar_url String - URL of the member's avatar image.
is_2fa_enabled Boolean - Represents whether two-factor authentication is enabled for the member.
has_sso Boolean - Represents whether the member uses SSO.
has_password Boolean - Represents whether the member has a password set.
role String - Role of the member: owner, admin, editor, or viewer.
permissions Array of permission objects - List of permissions.

Get a member

GET /accounts/:account_id/members/:member_id

curl https://api.userflow.com/accounts/3f2a4b56-7c8d-9e0f-1a2b-3c4d5e6f7a8b/members/3d7f23a1-0c24-4b94-9e39-57b93ccfa08e \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
  "object": "member",
  "name": "John Doe",
  "email": "john@example.com",
  "created_at": "2023-11-19T11:34:48.000+00:00",
  "avatar_url": "https://storage.googleapis.com/avatar.jpg",
  "is_2fa_enabled": true,
  "has_sso": true,
  "has_password": false,
  "role": "owner",
  "permissions": []
}

Retrieves a member by its ID within the specified account.

URL arguments

Key Description
account_id* String - Unique identifier for the account.
member_id* String - Unique identifier for the member.

* Required

Response body

Returns the member object, if found. Responds with 404 Not Found if not.

List members

GET /accounts/:account_id/members

curl https://api.userflow.com/accounts/3f2a4b56-7c8d-9e0f-1a2b-3c4d5e6f7a8b/members \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
      "object": "member",
      "name": "John Doe",
      "email": "john@example.com",
      "created_at": "2023-11-19T11:34:48.000+00:00",
      "avatar_url": "https://storage.googleapis.com/avatar.jpg",
      "is_2fa_enabled": true,
      "has_sso": true,
      "has_password": false,
      "role": "admin",
      "permissions": [
        {
          "action": "manage_team",
          "subject": "account",
          "subject_id": "81d3e210-50c0-57d6-3374-8bcgcdb3c5fb"
        }
      ]
    },
    ...
    {
      "id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
      "object": "member",
      ...
    }
  ],
  "has_more": true,
  "url": "/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/members",
  "next_page_url": "/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/members?starting_after=3d7f23a1-0c24-4b94-9e39-57b93ccfa08e"
}

Returns a list of members for the specified account.

URL arguments

Key Description
account_id* String - Unique identifier for the account.

* Required

Query parameters

Key Description
limit Number - See Pagination.
starting_after String - See Pagination.
order_by String or array of strings - See Ordering. Fields: created_at.
email String or array of strings - Only return members whose email equals this value.
role String or array of strings - Only include members whose role equals this value

Response body

Returns a list object with a data property that contains an array of member objects.

Update a member

PATCH /accounts/:account_id/members/:member_id

curl https://api.userflow.com/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/members/3d7f23a1-0c24-4b94-9e39-57b93ccfa08e \
  -XPATCH \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "role": "admin",
        "permissions": [
            {
                "action": "manage_team",
                "subject": "account",
                "subject_id": "81d3e210-50c0-57d6-3374-8bcgcdb3c5fb"
            }
        ]
      }'

Response:

{
  "id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
  "object": "member",
  "name": "John Doe",
  "email": "john@example.com",
  "created_at": "2023-11-19T11:34:48.000+00:00",
  "avatar_url": "https://storage.googleapis.com/avatar.jpg",
  "is_2fa_enabled": true,
  "has_sso": true,
  "has_password": false,
  "role": "admin",
  "permissions": [
    {
      "action": "manage_team",
      "subject": "account",
      "subject_id": "81d3e210-50c0-57d6-3374-8bcgcdb3c5fb"
    }
  ]
}

Updates an active account member's role and permissions within the specified account.

URL arguments

Key Description
account_id* String - Unique identifier for the account.
member_id* String - Unique identifier for the member.

* Required

Request body

Key Description
role** String - New role: admin, editor, or viewer.
permissions** Array of permission objects - A list of permissions that will replace the member’s existing permissions for an account. The value “*” can be used for the field subject_id to assign the publish_flow permission for all environments in the account.

** Requires one or both of role and permissions

Response body

Returns the updated member object.

Remove a member

DELETE /accounts/:account_id/members/:member_id

curl https://api.userflow.com/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/members/3d7f23a1-0c24-4b94-9e39-57b93ccfa08e \
  -XDELETE \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
  "object": "member",
  "deleted": true
}

Removes an active member from a Userflow account.

URL arguments

Key Description
account_id* String - Unique identifier for the account.
member_id* String - Unique identifier for the member.

* Required

Response body

Returns an object with a deleted key on success.

Invites

An invite represents a pending or expired invitation to join a Userflow account as a member. Invites do not have member IDs until accepted. Invites can contain roles and permissions which will be assigned to the team member when joining the account.

The invite object

Example invite object:

{
  "id": "e4d9916e-c5c3-4aa7-998b-8a7b1ff014c2",
  "object": "invite",
  "name": "Jane Doe",
  "email": "jane@example.com",
  "sender_id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "is_expired": false,
  "role": "editor",
  "permissions": [
    {
      "action": "publish_flow",
      "subject": "environment",
      "subject_id": "9a3c5e3e-7c4f-4a83-88be-86a3c85fa0e4"
    }
  ]
}
Key Description
id String - Unique identifier for the invite.
object String - Represents the object's type. Always invite.
name String - Name of the invited person.
email String - Email address of the invited person.
sender_id String - ID of the member who sent the invite. Can be null if the member who sent the invite is no longer a part of the Userflow account.
created_at String - ISO 8601 datetime representing when the invite was created.
is_expired Boolean - Represents whether the invite has expired.
role String - Intended role: admin, editor, or viewer.
permissions Array of permission objects - Intended permissions.

Get an invite

GET /accounts/:account_id/invites/:invite_id

curl https://api.userflow.com/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/invites/e4d9916e-c5c3-4aa7-998b-8a7b1ff014c2 \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "e4d9916e-c5c3-4aa7-998b-8a7b1ff014c2",
  "object": "invite",
  "name": "Jane Doe",
  "email": "jane@example.com",
  "sender_id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "is_expired": true,
  "role": "editor",
  "permissions": []
}

Retrieves an invite by its ID within the specified account.

URL arguments

Key Description
account_id* String - Unique identifier for the account.
invite_id* String - Unique identifier for the invite.

* Required

Response body

Returns the invite object, if found. Responds with 404 Not Found if not.

List invites

GET /accounts/:account_id/invites

curl https://api.userflow.com/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/invites \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "object": "list",
  "data": [
    {
      "id": "e4d9916e-c5c3-4aa7-998b-8a7b1ff014c2",
      "object": "invite",
      "name": "Jane Doe",
      "email": "jane@example.com",
      "sender_id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
      "created_at": "2022-10-17T12:34:56.000+00:00",
      "is_expired": false,
      "role": "admin",
      "permissions": [
        {
          "action": "manage_team",
          "subject": "account",
          "subject_id": "81d3e210-50c0-57d6-3374-8bcgcdb3c5fb"
        }
      ]
   },
    ...
   {
      "id": "5763f54a-d1f7-44ae-b00a-184a881cb66c",
      "object": "invite",
      ...
    }
  ],
  "has_more": true,
  "url": "/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/invites",
  "next_page_url": "/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/invites?starting_after=5763f54a-d1f7-44ae-b00a-184a881cb66c"
}

Returns a list of all pending and expired invites created in a Userflow account.

URL arguments

Key Description
account_id* String - Unique identifier for the account.

* Required

Query parameters

Key Description
limit Number - See Pagination.
starting_after String - See Pagination.
order_by String or array of strings - See Ordering. Fields: name, created_at. Defaults to created_at.
email String or array of strings - Only return invites whose invitee email equals this value.
is_expired Boolean - Only return invites whose is_expired  equals this value.

Response body

Returns a list object with a data property that contains an array of invite objects.

Create an invite

POST /accounts/:account_id/invites

curl https://api.userflow.com/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/invites \
  -XPOST \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03' \
  -H 'Content-Type: application/json' \
  -d '{
        "email": "john.doe@email.com",
        "name" : "John Doe",
        "role" : "editor",
        "permissions": [
          {
            "action": "publish_flow",
            "subject": "environment",
            "subject_id": "*"
          }
        ]
      }'

Response:

{
  "id": "e4d9916e-c5c3-4aa7-998b-8a7b1ff014c2",
  "object": "invite",
  "name": "John Doe",
  "email": "john.doe@email.com",
  "sender_id": "3d7f23a1-0c24-4b94-9e39-57b93ccfa08e",
  "created_at": "2022-10-17T12:34:56.000+00:00",
  "is_expired": false,
  "role": "editor",
  "permissions": [
    {
      "action": "publish_flow",
      "subject": "environment",
      "subject_id": "9a3c5e3e-7c4f-4a83-88be-86a3c85fa0e4"
    },
    {
      "action": "publish_flow",
      "subject": "environment",
      "subject_id": "7a9fd5c6-6fd2-449f-b378-2ff90f3e93c1"
    }
  ]
}

Creates and sends an email invite to join a Userflow account. If an expired invite is already present for the invite email, then a new invite will be freshly created. If a pending invite is already present for the invite email or if a member with the same email is already part of the account, then a new invite will not be created and a 400 error response will be returned with appropriate message and code values.

URL arguments

Key Description
account_id* String - Unique identifier for the account.

* Required

Request body

Key Description
name* String - Name of the invited person.
email* String - Email address of the invited person.
role* String - Intended role: admin, editor, or viewer.
permissions Array of permission objects - Intended permissions for the invited person. The value “*” can be used for the field subject_id to assign the publish_flow permission for all environments in the account.

* Required

Response body

Returns the newly created invite object.

Delete an invite

DELETE /accounts/:account_id/invites/:invite_id

curl https://api.userflow.com/accounts/81d3e210-50c0-57d6-3374-8bcgcdb3c5fb/invites/e4d9916e-c5c3-4aa7-998b-8a7b1ff014c2 \
  -XDELETE \
  -H 'Authorization: Bearer <personal-api-key>' \
  -H 'Userflow-Version: 2020-01-03'

Response:

{
  "id": "e4d9916e-c5c3-4aa7-998b-8a7b1ff014c2",
  "object": "invite",
  "deleted": true
}

Deletes an already created invite from an account.

URL arguments

Key Description
account_id* String - Unique identifier for the account.
invite_id* String - Unique identifier for the invite.

* Required

Response body

Returns an object with a deleted key on success.

Permissions

A permission represents a specific access right granted to a member or invite within a particular account or environment. The structure includes the fields action, subject, and subject_id, providing granularity and flexibility. Permissions are present only if granted (e.g., no entry for denied permissions).

The currently supported permisson actions are manage_team for admins and publish_flow for editors. An empty permissions array is used in member or invite objects for owner and viewer roles.

Account-scoped permissions (e.g., "manage_team") specify "account" in the subject field and the account UUID in the subject_id field, while Environment-scoped permissions (e.g., "publish_flow") specify "environment" in the subject field and use the environment UUID in the subject_id field.

The permission object

Example permission object (account-scoped):

{
  "action": "manage_team",
  "subject": "account",
  "subject_id": "81d3e210-50c0-57d6-3374-8bcgcdb3c5fb"
}

Example permission object (environment-scoped):

{
  "action": "publish_flow",
  "subject": "environment",
  "subject_id": "9a3c5e3e-7c4f-4a83-88be-86a3c85fa0e4"
}
Key Description
action String - Action permitted: manage_team or publish_flow.
subject String - Subject of the permission: account or environment.
subject_id String - ID of the subject (account ID or environment ID).