Distribution API
Every project exposes two public URLs: the subscriber portal and the deep link into the project's connector installation, with a start payload that decides what opens.
Every project exposes two public URLs: the subscriber portal and the deep link into the project's connector installation, with a start payload that decides what opens. These endpoints return the canonical strings so clients do not reconstruct them from project, installation and plan fields themselves; if the URL shapes ever change, REST callers get the update for free. The connector shapes its own links; nothing here spells a platform's URL.
Endpoints
/v1/projects/{project}/distribution/deep-linkBuild a deep linkGET/v1/projects/{project}/distribution/portal-urlGet the portal URLBuilds a start link against the project's connected installation. The payload depends on which query parameter you pass (at most one; an access code wins over a plan, and a plan over a custom parameter):
| Query parameter | Resulting payload | Acted on by the bot? |
|---|---|---|
access_code | the code verbatim | Yes: redeemed on open. |
plan_id | the plan's bare UUID | Yes: opens that plan's checkout flow. |
custom | the caller-supplied string | No, see below. |
| (none) | project_<uuid> | No: falls through to the project home, which is the intent. |
Only a bare UUID is acted on. The bot acts on a start payload only when it is a bare UUID: it is matched against the project's plans first, then tried as an access code.
custompayloads and the defaultproject_<uuid>do not match that shape, so the bot ignores them and opens its home screen.customis therefore a passthrough for your own tracking: the platform delivers it and you can read it off the update in a webhook consumer, but Subscriby neither stores it nor attaches it to the member.
curl "https://api.subscriby.net/v1/projects/$PROJECT_ID/distribution/deep-link?plan_id=$PLAN_ID" \ -H "Authorization: Bearer $SUBSCRIBY_TOKEN"Requires ability
The token must hold this ability, or the call is refused with 403.
MCP tool
Runs the same action from an agent, behind the same ability.
Authorization
bearerToken A personal access token minted on the dashboard under Settings, then Tokens, sent as Authorization: Bearer sbt_live_…. The token carries the abilities each endpoint lists under Requires ability and is frozen to one team.
In: header
Path Parameters
The project, resolved by the route binder.
uuidQuery Parameters
A plan of this project; the link then opens that plan's checkout. Answers 404 for a plan outside the project. Ignored when access_code is also sent.
uuidAn access code, carried verbatim and redeemed when the link is opened. Wins over plan_id and custom.
length <= 64Your own tracking string, 1 to 64 characters of letters, digits, underscore or hyphen. Delivered by the platform but not acted on or stored by Subscriby. Used only when neither access_code nor plan_id is sent.
^[A-Za-z0-9_-]{1,64}$Responses
200OKapplication/json
The bot URL, the start payload and the assembled deep link.
401UnauthorizedAUTHENTICATION_REQUIREDapplication/json
The request carries no bearer token, or one that is revoked, malformed, or minted for another environment (an sbt_test_ token on production).
403ForbiddenTOKEN_MISSING_ABILITYapplication/json
The token is valid but does not carry the ability this endpoint requires; error.context.required_ability names the one to grant. An endpoint that also checks who owns a row or which tier the account is on answers FORBIDDEN, TEAM_TIER_REQUIRED or CONNECTOR_TIER_REQUIRED with the same status, and says so in its own description.
404Not foundRESOURCE_NOT_FOUNDapplication/json
An id in the path names nothing the token can see. TENANT_MISMATCH: the project sits outside the token's scope:project: allow-list, or the token carries no team scope. Both answer 404 rather than 403 so that existence outside the token's scope cannot be inferred.
On this endpoint: RESOURCE_NOT_FOUND: when plan_id does not belong to this project.
422Validation failedVALIDATION_FAILEDapplication/json
The payload broke a rule, and error.fields maps each offending key to its messages. A refusal from the domain, such as a plan that cannot go on sale or a member who cannot be removed, uses the same code with error.message saying why and no fields.
On this endpoint: VALIDATION_FAILED: with reason: no_bot_attached when the project has no connected installation to link to. VALIDATION_FAILED: on plan_id when it is not a UUID, or on custom when it is not 1 to 64 characters of letters, digits, underscore or hyphen.
429Too many requestsRATE_LIMITEDapplication/json
The token has spent its 300 requests a minute or 10,000 an hour; Retry-After says when the next one is accepted.
The portal URL always uses the project's current handle. If you change the handle the portal URL updates in lock-step; any old link answers 404 on the portal host.
Requires ability
The token must hold this ability, or the call is refused with 403.
MCP tool
Runs the same action from an agent, behind the same ability.
Authorization
bearerToken A personal access token minted on the dashboard under Settings, then Tokens, sent as Authorization: Bearer sbt_live_…. The token carries the abilities each endpoint lists under Requires ability and is frozen to one team.
In: header
Path Parameters
The project, resolved by the route binder.
uuidResponses
200OKapplication/json
The portal URL for the project's handle.
401UnauthorizedAUTHENTICATION_REQUIREDapplication/json
The request carries no bearer token, or one that is revoked, malformed, or minted for another environment (an sbt_test_ token on production).
403ForbiddenTOKEN_MISSING_ABILITYapplication/json
The token is valid but does not carry the ability this endpoint requires; error.context.required_ability names the one to grant. An endpoint that also checks who owns a row or which tier the account is on answers FORBIDDEN, TEAM_TIER_REQUIRED or CONNECTOR_TIER_REQUIRED with the same status, and says so in its own description.
404Not foundRESOURCE_NOT_FOUNDapplication/json
An id in the path names nothing the token can see. TENANT_MISMATCH: the project sits outside the token's scope:project: allow-list, or the token carries no team scope. Both answer 404 rather than 403 so that existence outside the token's scope cannot be inferred.
429Too many requestsRATE_LIMITEDapplication/json
The token has spent its 300 requests a minute or 10,000 an hour; Retry-After says when the next one is accepted.
Related
How is this guide?
Disaster Recovery API
Everything the Disaster Recovery pages show a creator is readable here: what the health probes found broken, what was done about it and whether it can still be undone, how many members a channel recovery has re-admitted so far, how many self-service recoveries the rolling window still allows, and how ready the account is for the next ban.
Groups API
A group is a named bundle of permissions on a team that several collaborators hold at once: put the three people who run the inbox into a Support group and they share its permissions, take one out and they keep only what their own role gives them.