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
- The buyer identifier (
guid) in the app: How we recognise the customer. - An access key on your server: Authentication. The request needs a key, so the app asks your server for properties, not us.
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-…" }
]
}
| Field | Type | What it is |
|---|---|---|
guid | string | buyer identifier |
user_id | string | the same value as guid |
email | string or null | the customer's email; null means we have none. It is not repeated in the property list |
properties | array | buyer properties; empty if there are none |
property | string | property name |
value | string | value, always a string, even for a number |
block_id | string or null | quiz question ID; null for properties not from the quiz |
The list has three kinds of properties:
- answers to quiz questions: the property name equals the question text the customer saw;
- campaign tags
utm_source…utm_content, from the first visit to the funnel: a repeat visit with other tags does not overwrite them; country: the country by IP address at the first visit, as a two-letter code.
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 question | What comes in value |
|---|---|
| single choice | option ID, not its text |
| multiple choice | IDs separated by commas without spaces |
| rating or scale | a number as a string: 4 |
| weight or height | number and unit separated by a space: 70 kg |
| text or number | what 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
- The customer reached the paywall bypassing the quiz.
- They skipped an optional question or did not see it because of branching.
- They refused tracking on the funnel page, or in the EU did not consent to it. Then we save neither answers, nor tags, nor country.
- A quiz with a consent block for collecting answers: without that checkbox, answers on the following screens are not saved.
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" }'
| Where | Field | What to pass |
|---|---|---|
| address | user | buyer identifier. Exactly user: the guid parameter gets the refusal 400 here |
| body | property | property name, a string up to 255 characters |
| body | value | value, 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>}
| Tag | What 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:
- the answer to a question without the "Pass the answer to the outgoing address" checkbox in the editor. Only the "Single choice", "Multiple choice" and "Multi-step question" blocks have the checkbox: answers from scales, ratings and input fields do not go into the address;
- all answers in funnels about weight loss, health, mental health and faith: the checkbox has no effect there;
- all tags, if the visitor refused tracking or their browser forbids passing data (Global Privacy Control);
- the visitor identifier and answers, if the person came through an A/B test direction "Add a URL": there was no quiz on that path.
Email, phone, name and free text are not among the tags. The cabinet will not save a funnel with such a tag.
Next
- Check the entitlement: whether this customer's access is paid.
- Webhooks: learn about answers the moment the customer gives them: the events
user.property_updatedanduser.properties_completed. - How we recognise the customer: where the app gets the identifier to ask for properties by.