S Subster

Buyer properties

You will fetch the customer's quiz answers from our server and skip onboarding screens that ask the same.

A person took the web quiz, paid and opened the app, and onboarding asks their goal and age again. We already have the answers: the quiz saves them to the identifier the payment later goes to. One request from your server, and the app shows only what we do not know yet.

Properties say nothing about payment: access to paid content is decided by the entitlement check.

Before you start

Request and answer

curl "https://api.subster.ai/s2s/v1/user/properties?guid=<GUID>" \
  -H "Authorization: Bearer sk_live_…"
{
  "user_id": "abc123def456",
  "guid": "abc123def456",
  "email": "[email protected]",
  "properties": [
    { "property": "country", "value": "DE", "block_id": null },
    { "property": "utm_campaign", "value": "spring_sale", "block_id": null },
    { "property": "Какая у вас цель?", "value": "b7c1e0d2-…", "block_id": "3f9a41c8-…" },
    { "property": "Ваш вес", "value": "70 kg", "block_id": "8d20e6f5-…" }
  ]
}
FieldTypeWhat it is
guidstringbuyer identifier
user_idstringthe same value as guid
emailstring or nullthe customer's email; null means we have none. It is not repeated in the property list
propertiesarraybuyer properties; empty if there are none
propertystringproperty name
valuestringvalue, always a string, even for a number
block_idstring or nullquiz question ID; null for properties not from the quiz

The list has three kinds of properties:

An unknown identifier, or one from another project, even in your own organization, gets 404. A known customer without properties gets success and an empty list. Request-form refusals are as for the entitlement request with a key. The ceiling is 120 requests a minute per key; reading and writing have separate counters (Authentication).

Which screen to skip

Show an onboarding screen only if the list has no answer with its question ID (block_id). Match on block_id: the question text changes when the owner edits or translates it, block_id does not.

What is in value depends on the question:

Quiz questionWhat comes in value
single choiceoption ID, not its text
multiple choiceIDs separated by commas without spaces
rating or scalea number as a string: 4
weight or heightnumber and unit separated by a space: 70 kg
text or numberwhat the customer entered

block_id is enough to skip a screen. To use the answer itself, for example the chosen goal, map option IDs to your values:

{
  "3f9a41c8-…": { "b7c1e0d2-…": "lose_weight", "e04a9b31-…": "build_muscle" }
}

If the customer retakes the quiz with the same identifier, a new answer replaces the old one.

You can collect block_id and option IDs only by walking the funnel yourself: walk it, return to the app and request the properties; the answer has each question with the chosen option. One run gives one option per question, so three options need three runs, each with a different choice. The owner added an option? Walk the funnel again.

A funnel copy, including one for an A/B test, gets new question and option IDs. Collect them the same way and keep a mapping per funnel.

Why an answer may be missing

So do not remove onboarding entirely: skip only the screens whose answer came.

Write your own property

An answer given in the app can be stored next to the quiz answers; then the whole profile comes in one request.

curl -X POST "https://api.subster.ai/s2s/v1/user/properties?user=<GUID>" \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "property": "onboarding_goal", "value": "strength" }'
WhereFieldWhat to pass
addressuserbuyer identifier. Exactly user: the guid parameter gets the refusal 400 here
bodypropertyproperty name, a string up to 255 characters
bodyvaluevalue, a string up to 10,000 characters. Pass a number as a string: "5", not 5

The answer is { "success": true }. One request writes one property: several properties take several requests. Writing the same name again replaces the value; block_id of your own property is null.

Seven names are written only by us: utm_source, utm_medium, utm_campaign, utm_term, utm_content, country, email. Writing such a name gets 400 with the code reserved_property. If the customer explicitly refused tracking, the write gets 409 with the code consent_denied.

If the funnel sends the person to your site

A funnel can end not with a payment with us but by moving to your address: a link button, the "Redirect to URL" block or a navigation rule. The same data then arrives as address parameters, with no request to us.

The project owner puts tags in curly braces into the address, and we fill in the values as the person leaves. The tag list is under the address field, in the "Labels in the outgoing address" hint.

https://your-site.io/pay?src={utm_source}&cid={click_id}&goal={answer.<question-id>}
TagWhat arrives
{utm_source}, {utm_medium}, {utm_campaign}, {utm_content}, {utm_term}campaign tags from the address by which the person came to the funnel
{click_id}the value of the click id the person came with: a partner one (click_id, sub_id or clickid), and if there is none and the visitor agreed to tracking, gclid, fbclid or ttclid. One value comes, without the tag name: the address does not show which network gave it
{visitor_id}the visitor's permanent identifier in this browser
{answer.<question-id>}ID of the chosen option; several options separated by commas. The question ID is the same block_id as in the property list

Tags are filled in only in parameter values. If a tag has no value and the parameter is only that tag, like cid={click_id}, the parameter disappears entirely: your page must cope without it.

These come empty:

Email, phone, name and free text are not among the tags. The cabinet will not save a funnel with such a tag.

Next