NAV Navbar

Userflow API Reference

Userflow API endpoint:

https://api.userflow.com

With the Userflow REST API you can synchronize your user data with Userflow, and track events directly from your back-end application.

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.

Follow us on Twitter for API announcements.

Quick links

Quick access to endpoints that you'll probably need:

Authentication

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

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

Example:

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

You use API keys to authenticate your API requests. You can view and create 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.

API keys grant access to a whole environment in your account! Make sure to keep them 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.

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.
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, 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, 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.

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