API reference

Every endpoint of the public REST API, generated from the same schemas the server validates with. Authenticate with an API key from your settings, or with an OAuth access token.

Authenticated API requests are limited to 300 requests per 60 seconds per credential. Every response carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (seconds until the window resets); a 429 also includes retry-after.

Base URLhttps://nextjs-saas-template.lubomirgeorgiev.com
v1.0.0
19 of 19 endpoints

Account

GET/api/v1/me

Get the authenticated account

Returns the profile of the account the credential belongs to: id, email, name, role, avatar, preferred locale, and verification/creation timestamps. Account-level: a team-scoped API key is refused with 403.

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/me" \
  -H "Authorization: Bearer $API_KEY"

Response

200The authenticated account.object
idstringRequired
emailstringRequirednullable
firstNamestringRequirednullable
lastNamestringRequirednullable
rolestringRequired
avatarstringRequirednullable
preferredLocalestringRequirednullable
emailVerifiedstringRequirednullableformat: date-time
createdAtstringRequiredformat: date-time
updatedAtstringRequiredformat: date-time
Example
{
  "id": "string",
  "email": "string",
  "firstName": "string",
  "lastName": "string",
  "role": "string",
  "avatar": "string",
  "preferredLocale": "string",
  "emailVerified": "2026-01-01T00:00:00.000Z",
  "createdAt": "2026-01-01T00:00:00.000Z",
  "updatedAt": "2026-01-01T00:00:00.000Z"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
PATCH/api/v1/me

Update the authenticated account

Updates the first and last name of the authenticated account. Both fields are required; the response is the account as it stands after the update. Account-level: a team-scoped API key is refused with 403.

Request body

firstNamestringRequiredminLength 2maxLength 255
lastNamestringRequiredminLength 2maxLength 255

Request

curl
curl -X PATCH "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/me" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "firstName": "string",
  "lastName": "string"
}'

Response

200The updated account.object
idstringRequired
emailstringRequirednullable
firstNamestringRequirednullable
lastNamestringRequirednullable
rolestringRequired
avatarstringRequirednullable
preferredLocalestringRequirednullable
emailVerifiedstringRequirednullableformat: date-time
createdAtstringRequiredformat: date-time
updatedAtstringRequiredformat: date-time
Example
{
  "id": "string",
  "email": "string",
  "firstName": "string",
  "lastName": "string",
  "role": "string",
  "avatar": "string",
  "preferredLocale": "string",
  "emailVerified": "2026-01-01T00:00:00.000Z",
  "createdAt": "2026-01-01T00:00:00.000Z",
  "updatedAt": "2026-01-01T00:00:00.000Z"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
GET/api/v1/me/sessions

List sign-in sessions

Lists the account's active browser/app sign-in sessions, newest first, with the device and location recorded at sign-in. `isCurrentSession` is false for every bearer credential. Account-level: a team-scoped API key is refused with 403.

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/me/sessions" \
  -H "Authorization: Bearer $API_KEY"

Response

200The account's sessions.object[]
idstringRequired
createdAtstringRequiredformat: date-time
expiresAtstringRequiredformat: date-time
isCurrentSessionbooleanRequired
authenticationTypestringRequirednullable
countrystringRequirednullable
citystringRequirednullable
browserstringRequirednullable
osstringRequirednullable
deviceTypestringRequirednullable
Example
[
  {
    "id": "string",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "isCurrentSession": true,
    "authenticationType": "string",
    "country": "string",
    "city": "string",
    "browser": "string",
    "os": "string",
    "deviceType": "string"
  }
]
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
DELETE/api/v1/me/sessions/{sessionId}

Revoke a sign-in session

Signs the account out of one session. Revocation is scoped to the caller's own sessions, so an unknown session id is a no-op rather than an error. Account-level: a team-scoped API key is refused with 403.

Parameters

sessionIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl -X DELETE "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/me/sessions/{sessionId}" \
  -H "Authorization: Bearer $API_KEY"

Response

200The session was revoked.object
successunknownRequired
Example
{
  "success": "string"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}

Teams

GET/api/v1/teams

List the caller's teams

Lists every team the authenticated account is an active member of, with the account's role in each. Teams whose membership is inactive or expired are omitted. A team-scoped API key lists only the single team it is scoped to.

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams" \
  -H "Authorization: Bearer $API_KEY"

Response

200The caller's teams.object[]
idstringRequired
namestringRequired
slugstringRequired
descriptionstringRequirednullable
avatarUrlstringRequirednullable
roleobjectRequired
idstringRequired
namestringRequired
Example
[
  {
    "id": "string",
    "name": "string",
    "slug": "string",
    "description": "string",
    "avatarUrl": "string",
    "role": {
      "id": "string",
      "name": "string"
    }
  }
]
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
POST/api/v1/teams

Create a team

Creates a team owned by the authenticated account, which also becomes its first member. The slug is derived from the name. Fails with 403 when the account is at its team limit. Account-level: a team-scoped API key is refused with 403.

Request body

namestringRequiredminLength 1maxLength 100
descriptionstringOptionalmaxLength 1000

Request

curl
curl -X POST "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "description": "string"
}'

Response

201The created team.object
idstringRequired
namestringRequired
slugstringRequired
descriptionstringRequirednullable
avatarUrlstringRequirednullable
roleobjectRequired
idstringRequired
namestringRequired
Example
{
  "id": "string",
  "name": "string",
  "slug": "string",
  "description": "string",
  "avatarUrl": "string",
  "role": {
    "id": "string",
    "name": "string"
  }
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
GET/api/v1/teams/{teamId}

Get a team

Returns one team the authenticated account belongs to. Teams the account is not an active member of answer 404, never 403, so team ids cannot be probed.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}" \
  -H "Authorization: Bearer $API_KEY"

Response

200The team.object
idstringRequired
namestringRequired
slugstringRequired
descriptionstringRequirednullable
avatarUrlstringRequirednullable
roleobjectRequired
idstringRequired
namestringRequired
Example
{
  "id": "string",
  "name": "string",
  "slug": "string",
  "description": "string",
  "avatarUrl": "string",
  "role": {
    "id": "string",
    "name": "string"
  }
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
PATCH/api/v1/teams/{teamId}

Rename a team

Changes a team's display name. Requires the `edit_team_settings` permission on that team. The slug is deliberately left alone so existing links and invitations keep working.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255

Request body

namestringRequiredminLength 1maxLength 100

Request

curl
curl -X PATCH "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string"
}'

Response

200The renamed team.object
idstringRequired
namestringRequired
slugstringRequired
descriptionstringRequirednullable
avatarUrlstringRequirednullable
roleobjectRequired
idstringRequired
namestringRequired
Example
{
  "id": "string",
  "name": "string",
  "slug": "string",
  "description": "string",
  "avatarUrl": "string",
  "role": {
    "id": "string",
    "name": "string"
  }
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}

Members

GET/api/v1/teams/{teamId}/members

List team members

Lists every membership of a team with the member's identity, role, and join date. Requires the `access_dashboard` permission on that team.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}/members" \
  -H "Authorization: Bearer $API_KEY"

Response

200The team's members.object[]
membershipIdstringRequired
userIdstringRequired
emailstringRequirednullable
firstNamestringRequirednullable
lastNamestringRequirednullable
avatarstringRequirednullable
roleIdstringRequired
roleNamestringRequirednullable
isSystemRolebooleanRequired
isActivebooleanRequired
joinedAtstringRequirednullableformat: date-time
Example
[
  {
    "membershipId": "string",
    "userId": "string",
    "email": "string",
    "firstName": "string",
    "lastName": "string",
    "avatar": "string",
    "roleId": "string",
    "roleName": "string",
    "isSystemRole": true,
    "isActive": true,
    "joinedAt": "2026-01-01T00:00:00.000Z"
  }
]
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
DELETE/api/v1/teams/{teamId}/members/{userId}

Remove a team member

Removes a member from a team. Requires the `remove_members` permission on that team; the team owner can never be removed this way.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255
userIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl -X DELETE "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}/members/{userId}" \
  -H "Authorization: Bearer $API_KEY"

Response

200The member was removed.object
successunknownRequired
Example
{
  "success": "string"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}

Invitations

GET/api/v1/teams/{teamId}/roles

List the roles a team can assign

Lists every role id accepted by createTeamInvitation, with the permissions each one grants. System roles (`owner`, `member`, `guest`) exist on every team and are passed with `isSystemRole: true`; roles the team defined itself are passed with `isSystemRole: false`. `isAssignable` is false for roles an invitation cannot grant, such as `owner`. Requires the `access_dashboard` permission.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}/roles" \
  -H "Authorization: Bearer $API_KEY"

Response

200The roles this team can assign.object[]
roleIdstringRequired
namestringRequirednullable
isSystemRolebooleanRequired
isAssignablebooleanRequired
permissionsstring[]Required
Example
[
  {
    "roleId": "string",
    "name": "string",
    "isSystemRole": true,
    "isAssignable": true,
    "permissions": [
      "string"
    ]
  }
]
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
GET/api/v1/teams/{teamId}/invitations

List pending team invitations

Lists invitations that have not been accepted and have not expired. Requires the `invite_members` permission; callers without it receive an empty list, never invitee emails.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}/invitations" \
  -H "Authorization: Bearer $API_KEY"

Response

200The team's pending invitations.object[]
idstringRequired
emailstringRequired
roleIdstringRequired
roleNamestringRequirednullable
isSystemRolebooleanRequired
createdAtstringRequiredformat: date-time
expiresAtstringRequiredformat: date-time
Example
[
  {
    "id": "string",
    "email": "string",
    "roleId": "string",
    "roleName": "string",
    "isSystemRole": true,
    "createdAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  }
]
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
POST/api/v1/teams/{teamId}/invitations

Invite someone to a team

Sends an invitation email for a team seat. `roleId` is optional and defaults to `member`; call listTeamRoles for the other ids this team accepts, and pass `isSystemRole: false` alongside a role the team defined itself. Requires the `invite_members` permission and a free seat on the team's plan — inviting past the seat limit answers 403, and the response detail names the limit. The response never reveals whether the address already has an account or is already a member.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255

Request body

emailstringRequiredformat: emailminLength 1maxLength 255
roleIdstringOptionalminLength 1maxLength 255default: "member"
isSystemRolebooleanOptionaldefault: true

Request

curl
curl -X POST "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}/invitations" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "user@example.com",
  "roleId": "member",
  "isSystemRole": true
}'

Response

201The invitation was sent.object
successunknownRequired
Example
{
  "success": "string"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
DELETE/api/v1/teams/{teamId}/invitations/{invitationId}

Revoke a pending invitation

Deletes a pending invitation so its link stops working. Requires the `invite_members` permission. An already accepted or unknown invitation answers 404.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255
invitationIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl -X DELETE "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}/invitations/{invitationId}" \
  -H "Authorization: Bearer $API_KEY"

Response

200The invitation was revoked.object
successunknownRequired
Example
{
  "success": "string"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}

Billing

GET/api/v1/teams/{teamId}/billing

Get a team's subscription

Read-only summary of a team's plan, subscription status, billing interval, add-on units, and renewal date. Requires the `access_billing` permission. Checkout and plan changes are not exposed over the API.

Parameters

teamIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/teams/{teamId}/billing" \
  -H "Authorization: Bearer $API_KEY"

Response

200The team's subscription.object
planIdstringRequired
planNamestringRequired
statusstringRequirednullable
intervalstringRequirednullable
addonsobjectRequired
planExpiresAtstringRequirednullableformat: date-time
cancelAtPeriodEndbooleanRequired
needsPaymentActionbooleanRequired
Example
{
  "planId": "string",
  "planName": "string",
  "status": "string",
  "interval": "string",
  "addons": {},
  "planExpiresAt": "2026-01-01T00:00:00.000Z",
  "cancelAtPeriodEnd": true,
  "needsPaymentAction": true
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}

API keys

GET/api/v1/api-keys

List API keys

Lists the caller's personal API keys, or a team's keys when `teamId` is given (which requires the `manage_api_keys` permission on that team). Revoked keys are never listed, and no response can ever contain a key's secret. Account-level: a team-scoped API key is refused with 403.

Parameters

teamIdstringOptionalin: queryminLength 1maxLength 255

Request

curl
curl "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/api-keys" \
  -H "Authorization: Bearer $API_KEY"

Response

200The matching API keys.object[]
idstringRequired
namestringRequired
keyPrefixstringRequired
last4stringRequired
scopesstring[]Required
teamIdstringRequirednullable
createdAtstringRequiredformat: date-time
lastUsedAtstringRequirednullableformat: date-time
expiresAtstringRequirednullableformat: date-time
Example
[
  {
    "id": "string",
    "name": "string",
    "keyPrefix": "string",
    "last4": "string",
    "scopes": [
      "string"
    ],
    "teamId": "string",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "lastUsedAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  }
]
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
POST/api/v1/api-keys

Create an API key

Creates an API key for the caller, or for a team when `teamId` is given. The scopes requested must be a subset of the calling credential's own scopes. The `secret` in the response is returned exactly once and is not recoverable afterwards. Account-level: a team-scoped API key is refused with 403.

Request body

namestringRequiredminLength 1maxLength 100
scopesstring[]RequiredminItems 1maxItems 10
profile:readprofile:writeteams:readteams:writemembers:readmembers:writeinvites:writebilling:readapi-keys:readapi-keys:write
teamIdstringOptionalminLength 1maxLength 255
expiresInDaysintegerOptionalminimum 1maximum 365

Request

curl
curl -X POST "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/api-keys" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "scopes": [
    "profile:read"
  ],
  "teamId": "string",
  "expiresInDays": 0
}'

Response

201The created key and its one-time secret.object
keyobjectRequired
idstringRequired
namestringRequired
keyPrefixstringRequired
last4stringRequired
scopesstring[]Required
teamIdstringRequirednullable
createdAtstringRequiredformat: date-time
lastUsedAtstringRequirednullableformat: date-time
expiresAtstringRequirednullableformat: date-time
secretstringRequired
Example
{
  "key": {
    "id": "string",
    "name": "string",
    "keyPrefix": "string",
    "last4": "string",
    "scopes": [
      "string"
    ],
    "teamId": "string",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "lastUsedAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  },
  "secret": "string"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
PATCH/api/v1/api-keys/{keyId}

Update an API key's scopes

Replaces the scopes granted to an API key; the list is not merged with the key's current scopes, so send the full set you want it to end up with. The new scopes must be a subset of the calling credential's own scopes. The change takes effect immediately, including for the key making the call. Keys belonging to another account, and revoked keys, answer 404 so key ids cannot be probed. Account-level: a team-scoped API key is refused with 403.

Parameters

keyIdstringRequiredin: pathminLength 1maxLength 255

Request body

scopesstring[]RequiredminItems 1maxItems 10
profile:readprofile:writeteams:readteams:writemembers:readmembers:writeinvites:writebilling:readapi-keys:readapi-keys:write

Request

curl
curl -X PATCH "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/api-keys/{keyId}" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "scopes": [
    "profile:read"
  ]
}'

Response

200The updated key.object
idstringRequired
namestringRequired
keyPrefixstringRequired
last4stringRequired
scopesstring[]Required
teamIdstringRequirednullable
createdAtstringRequiredformat: date-time
lastUsedAtstringRequirednullableformat: date-time
expiresAtstringRequirednullableformat: date-time
Example
{
  "id": "string",
  "name": "string",
  "keyPrefix": "string",
  "last4": "string",
  "scopes": [
    "string"
  ],
  "teamId": "string",
  "createdAt": "2026-01-01T00:00:00.000Z",
  "lastUsedAt": "2026-01-01T00:00:00.000Z",
  "expiresAt": "2026-01-01T00:00:00.000Z"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}
DELETE/api/v1/api-keys/{keyId}

Revoke an API key

Revokes an API key immediately, including the key making the call. Keys belonging to another account answer 404 so key ids cannot be probed. Account-level: a team-scoped API key is refused with 403.

Parameters

keyIdstringRequiredin: pathminLength 1maxLength 255

Request

curl
curl -X DELETE "https://nextjs-saas-template.lubomirgeorgiev.com/api/v1/api-keys/{keyId}" \
  -H "Authorization: Bearer $API_KEY"

Response

200The key was revoked.object
successunknownRequired
Example
{
  "success": "string"
}
Error responses400401403404429Expand

Every failure is an RFC 9457 problem document whose code is stable and untranslated.

400
The request body, query, or path failed validation.
401
The credential is missing, malformed, expired, or revoked.
403
The credential lacks the required scope or the caller lacks the team permission.
404
The addressed resource does not exist or is not visible to this credential.
429
Rate limit exceeded; retry after the number of seconds in `retry-after`. The `RateLimit-*` headers describe the exhausted bucket.
Example
{
  "type": "string",
  "title": "string",
  "status": 0,
  "code": "string",
  "detail": "string",
  "requestId": "string",
  "retryAfter": 0,
  "errors": [
    {
      "in": "body",
      "pointer": "string",
      "code": "required",
      "params": {}
    }
  ]
}