Mint account-owner token (partner-only, rotate-on-mint)
Overview
Issue a Passport personal access token AS the owner user of a given account (account-owner impersonation). It is used for server-to-server calls to account-scoped endpoints on behalf of a tenant account. It rotates on each issue. Every other non-revoked token of the same name for that owner is revoked (at most one active account-owner token per owner).
Prerequisites
- A bearer token. Callable with Partner tokens.
Base URL
| Environment | URL |
|---|---|
| Production | https://production-api.shoutaboutus.com |
| Development | https://development-api.shoutaboutus.com |
Endpoint
POST /api/v1/partner/account-owner/token
Authentication
-
Requires a bearer token in the
Authorization: Bearer <bearer-token>header. -
Who can call it: Partner tokens.
-
Authentication sits inside the
partner/prefix. -
It is partner-agnostic (any partner, not only hipages — no partner-only access on this route).
-
The endpoint gates the caller in-controller.
-
The auth user's
person.companymust exist and havebundle_id === 1(partner), else 403Only partner users can mint brand-owner tokens. -
The target
account_idmust resolve to aCompanywithbundle_id === 3(an account) — else 404Company is not a brand. -
It must also descend from the caller's partner tree, else 403
Brand is not under your partner tree. -
The account must have an owner user (
getOwnerUser), else 404.
Rate limit
- 120 requests/min per caller.
Request body
- Body:
| Field | Type | Required | Description |
|---|---|---|---|
account_id | integer | yes | The account company id (must exist in the account's id with deleted_at null). Despite the field name, it must be an account-level company (bundle_id=3) under the caller's partner tree. Required for the only role that can call this (partner). |
{
"account_id": 4521
}Response
data.status(string) —success.data.token(string) — the issued Passport access token, usable as a bearer token for the account owner.data.account_owner(object):user_id(integer) — the account owner's user id.account_id(integer) — the account company id.expires_at(string|null) — ISO-8601 token expiry, or null when no Passport TTL is configured.prior_tokens_revoked(integer) — count of older same-name tokens revoked by this mint (rotation).
200 Success — fresh mint (no prior token) · 200
200{
"data": {
"status": "success",
"token": "<access-token>",
"account_owner": {
"user_id": 5,
"account_id": 42,
"expires_at": "2026-11-15T00:00:00+10:00",
"prior_tokens_revoked": 0
}
}
}When a non-revoked token for this owner already exists in the mint cache, the same token is returned unchanged with
prior_tokens_revoked: 0(no new mint, no rotation).
200 Success — rotation (prior token revoked) · 200
200{
"data": {
"status": "success",
"token": "<access-token>",
"account_owner": {
"user_id": 5,
"account_id": 42,
"expires_at": "2026-11-15T00:00:00+10:00",
"prior_tokens_revoked": 1
}
}
}403 Caller is not a partner · 403
403{
"message": "Forbidden",
"status": "error",
"errors": "Only partner users can mint brand-owner tokens."
}403 Account not in caller's tree · 403
403{
"message": "Forbidden",
"status": "error",
"errors": "Brand is not under your partner tree."
}404 Account not found / not an account / no owner · 404
404{
"message": "Not Found",
"status": "error",
"errors": "Brand has no owner user."
}422 Validation · 422
422{
"message": "The account id field is required.",
"errors": {
"account_id": ["The account id field is required."]
}
}Errors
| Status | Meaning |
|---|---|
401 | The bearer token is missing, expired or invalid |
403 | Caller is not a partner |
404 | Account not found / not an account / no owner |
422 | Validation |
500 | Unexpected server error |
Example request
curl --request POST \
--url "https://production-api.shoutaboutus.com/api/v1/partner/account-owner/token" \
--header 'Authorization: Bearer <bearer-token>' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"account_id": 4521}'Updated 11 days ago
