tenancy

Settings & Branches

Branches, tenant settings, feature flags, license status, and subscription status.

14 endpoints

GET /api/v1/branches

List every branch in the active tenant (any member may read).

Requires authenticationScopeNo additional permission

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/branches" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The tenant’s branches.
json
[
  {
    "id": "37386ae0-3738-7738-8386-37386ae03738",
    "tenantId": "4332af8b-4332-7332-832a-4332af8b4332",
    "name": "Algebra I",
    "code": "string",
    "address": {},
    "phone": "+15551234567",
    "isDefault": true,
    "isActive": true,
    "createdAt": "2026-03-02T09:00:00.000Z",
    "updatedAt": "2026-03-02T09:00:00.000Z"
  }
]
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

POST /api/v1/branches

Create a branch. Names must be unique per tenant.

Requires authenticationScopebranches.manage

Request body

NameTypeConstraints
namerequired
string1–200 chars
code
enum: "" | string
address
object
phone
enum: "" | string
isDefault
boolean

Example

json
{
  "name": "Algebra I",
  "code": "",
  "address": {},
  "phone": "",
  "isDefault": true
}

curl example

curl
curl -X POST "https://api.yourdomain.com/api/v1/branches" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Algebra I",
    "code": "",
    "address": {},
    "phone": "",
    "isDefault": true
  }'

Responses

201The newly created branch.
NameTypeConstraints
idrequired
string (uuid)
tenantIdrequired
string (uuid)
namerequired
string
coderequired
string | null≥1 chars
addressrequired
object | null
phonerequired
string | null≥1 chars
isDefaultrequired
boolean
isActiverequired
boolean
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "tenantId": "4332af8b-4332-7332-832a-4332af8b4332",
  "name": "Algebra I",
  "code": "string",
  "address": {},
  "phone": "+15551234567",
  "isDefault": true,
  "isActive": true,
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

DELETE /api/v1/branches/{id}

Delete a branch. The default branch, and any branch with role assignments, cannot be deleted (409).

Requires authenticationScopebranches.manage

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X DELETE "https://api.yourdomain.com/api/v1/branches/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The branch was deleted.
NameTypeConstraints
okrequired
enum: true
json
{
  "ok": true
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

PATCH /api/v1/branches/{id}

Update a branch. Setting `isDefault: true` clears the previous default branch.

Requires authenticationScopebranches.manage

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
name
string1–200 chars
code
enum: "" | string | null
address
object | null
phone
enum: "" | string | null
isDefault
boolean
isActive
boolean

Example

json
{
  "name": "Algebra I",
  "code": "",
  "address": {},
  "phone": "",
  "isDefault": true,
  "isActive": true
}

curl example

curl
curl -X PATCH "https://api.yourdomain.com/api/v1/branches/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Algebra I",
    "code": "",
    "address": {},
    "phone": "",
    "isDefault": true,
    "isActive": true
  }'

Responses

200The updated branch.
NameTypeConstraints
idrequired
string (uuid)
tenantIdrequired
string (uuid)
namerequired
string
coderequired
string | null≥1 chars
addressrequired
object | null
phonerequired
string | null≥1 chars
isDefaultrequired
boolean
isActiverequired
boolean
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "tenantId": "4332af8b-4332-7332-832a-4332af8b4332",
  "name": "Algebra I",
  "code": "string",
  "address": {},
  "phone": "+15551234567",
  "isDefault": true,
  "isActive": true,
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/feature-flags

Effective feature flags for the active tenant (registry defaults merged with the plan and tenant overrides).

Requires authenticationScopeNo additional permission

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/feature-flags" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The tenant’s effective feature flags.
NameTypeConstraints
basic_reportsrequired
boolean
advanced_reportsrequired
boolean
integration_apirequired
boolean
mcprequired
boolean
custom_domainrequired
boolean
whatsapp_notificationsrequired
boolean
sms_notificationsrequired
boolean
byo_email_providerrequired
boolean
byo_push_providerrequired
boolean
courses_videorequired
boolean
json
{
  "basic_reports": true,
  "advanced_reports": true,
  "integration_api": true,
  "mcp": true,
  "custom_domain": true,
  "whatsapp_notifications": true,
  "sms_notifications": true,
  "byo_email_provider": true,
  "byo_push_provider": true,
  "courses_video": true
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

PATCH /api/v1/feature-flags

Toggle feature flags. Platform-managed in cloud deployments (403 here); in self_hosted deployments the tenant owner may toggle self-manageable flags only.

Requires authenticationScopeNo additional permission

Request body

None.

Example

json
{}

curl example

curl
curl -X PATCH "https://api.yourdomain.com/api/v1/feature-flags" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'

Responses

200The tenant’s new effective feature flags.
NameTypeConstraints
basic_reportsrequired
boolean
advanced_reportsrequired
boolean
integration_apirequired
boolean
mcprequired
boolean
custom_domainrequired
boolean
whatsapp_notificationsrequired
boolean
sms_notificationsrequired
boolean
byo_email_providerrequired
boolean
byo_push_providerrequired
boolean
courses_videorequired
boolean
json
{
  "basic_reports": true,
  "advanced_reports": true,
  "integration_api": true,
  "mcp": true,
  "custom_domain": true,
  "whatsapp_notifications": true,
  "sms_notifications": true,
  "byo_email_provider": true,
  "byo_push_provider": true,
  "courses_video": true
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/license

This self-hosted instance’s license state (self_hosted deployments only).

Requires authenticationScopeNo additional permission

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/license" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The instance’s license summary.
NameTypeConstraints
planrequired
string
limitsrequired
object
featuresrequired
array<string>
expiresAtrequired
string (date-time)
staterequired
enum: "valid" | "expired" | "locked" | "revoked"
lockedByCommandrequired
boolean
lastServerContactAtrequired
string (date-time) | null
lastServerTimerequired
string (date-time) | null
consecutiveFailuresrequired
integer0–9007199254740991
installIdrequired
string
tamperSignalsrequired
array<object>
json
{
  "plan": "string",
  "limits": {},
  "features": [
    "string"
  ],
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "state": "valid",
  "lockedByCommand": true,
  "lastServerContactAt": "2026-03-02T09:00:00.000Z",
  "lastServerTime": "2026-03-02T09:00:00.000Z",
  "consecutiveFailures": 0,
  "installId": "2472867d-2472-7472-8728-2472867d2472",
  "tamperSignals": [
    {
      "kind": "state_signature_invalid",
      "detectedAt": "2026-03-02T09:00:00.000Z",
      "detail": "string"
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

POST /api/v1/license/apply-token

Apply a signed offline unlock token issued by the license server (self_hosted only).

Requires authenticationScopesettings.manage

Request body

NameTypeConstraints
tokenrequired
string1–8192 chars

Example

json
{
  "token": "string"
}

curl example

curl
curl -X POST "https://api.yourdomain.com/api/v1/license/apply-token" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "token": "string"
  }'

Responses

200The instance’s license summary after the token was applied.
NameTypeConstraints
planrequired
string
limitsrequired
object
featuresrequired
array<string>
expiresAtrequired
string (date-time)
staterequired
enum: "valid" | "expired" | "locked" | "revoked"
lockedByCommandrequired
boolean
lastServerContactAtrequired
string (date-time) | null
lastServerTimerequired
string (date-time) | null
consecutiveFailuresrequired
integer0–9007199254740991
installIdrequired
string
tamperSignalsrequired
array<object>
json
{
  "plan": "string",
  "limits": {},
  "features": [
    "string"
  ],
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "state": "valid",
  "lockedByCommand": true,
  "lastServerContactAt": "2026-03-02T09:00:00.000Z",
  "lastServerTime": "2026-03-02T09:00:00.000Z",
  "consecutiveFailures": 0,
  "installId": "2472867d-2472-7472-8728-2472867d2472",
  "tamperSignals": [
    {
      "kind": "state_signature_invalid",
      "detectedAt": "2026-03-02T09:00:00.000Z",
      "detail": "string"
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/license/updates

This install’s update channel status (self_hosted deployments only).

Requires authenticationScopesettings.manage

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/license/updates" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The install’s update status.
NameTypeConstraints
autoUpdaterequired
boolean
channelrequired
enum: "stable" | "beta"
currentVersionrequired
string
checkRequestedAtrequired
string (date-time) | null
updatesBlockedrequired
boolean
blockedReasonrequired
string | null
lastCheckAtrequired
string (date-time) | null
lastResultrequired
enum: "up_to_date" | "updated" | "rolled_back" | "failed" | "skipped" | null | null
reasonrequired
enum: "none" | "auto_update_off" | "min_from_version" | "locked" | "migration_running" | "manifest_unreachable" | "bad_signature" | "channel_mismatch" | "digest_mismatch" | "docker_error" | "health_check_failed" | "api_unreachable" | "setup_pending" | null | null
targetVersionrequired
string | null
detailrequired
string | null
updaterPresentrequired
boolean
json
{
  "autoUpdate": true,
  "channel": "stable",
  "currentVersion": "string",
  "checkRequestedAt": "2026-03-02T09:00:00.000Z",
  "updatesBlocked": true,
  "blockedReason": "string",
  "lastCheckAt": "2026-03-02T09:00:00.000Z",
  "lastResult": "up_to_date",
  "reason": "none",
  "targetVersion": "string",
  "detail": "string",
  "updaterPresent": true
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

POST /api/v1/license/updates/check

Ask the updater to check for a new release now instead of waiting for its next interval.

Requires authenticationScopesettings.manage

curl example

curl
curl -X POST "https://api.yourdomain.com/api/v1/license/updates/check" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The install’s update status, with the check request recorded.
NameTypeConstraints
autoUpdaterequired
boolean
channelrequired
enum: "stable" | "beta"
currentVersionrequired
string
checkRequestedAtrequired
string (date-time) | null
updatesBlockedrequired
boolean
blockedReasonrequired
string | null
lastCheckAtrequired
string (date-time) | null
lastResultrequired
enum: "up_to_date" | "updated" | "rolled_back" | "failed" | "skipped" | null | null
reasonrequired
enum: "none" | "auto_update_off" | "min_from_version" | "locked" | "migration_running" | "manifest_unreachable" | "bad_signature" | "channel_mismatch" | "digest_mismatch" | "docker_error" | "health_check_failed" | "api_unreachable" | "setup_pending" | null | null
targetVersionrequired
string | null
detailrequired
string | null
updaterPresentrequired
boolean
json
{
  "autoUpdate": true,
  "channel": "stable",
  "currentVersion": "string",
  "checkRequestedAt": "2026-03-02T09:00:00.000Z",
  "updatesBlocked": true,
  "blockedReason": "string",
  "lastCheckAt": "2026-03-02T09:00:00.000Z",
  "lastResult": "up_to_date",
  "reason": "none",
  "targetVersion": "string",
  "detail": "string",
  "updaterPresent": true
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/onboarding

First-run checklist for the active tenant: per-step done/count for the institute profile, branches, courses, groups, students, fees, notification channels and team. Cached 60s.

Requires authenticationScopesettings.manage

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/onboarding" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The tenant’s onboarding progress.
NameTypeConstraints
stepsrequired
array<object>
completedStepsrequired
integer0–9007199254740991
totalStepsrequired
integer0–9007199254740991
isCompleterequired
boolean
countsrequired
object
json
{
  "steps": [
    {
      "key": "institute",
      "done": true,
      "count": 0
    }
  ],
  "completedSteps": 0,
  "totalSteps": 0,
  "isComplete": true,
  "counts": {
    "branches": 0,
    "courses": 0,
    "groups": 0,
    "students": 0,
    "feeSchedules": 0,
    "invoices": 0,
    "staff": 0,
    "members": 0,
    "notificationChannels": 0
  }
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/settings

Effective settings for the active tenant (registry defaults merged with stored overrides).

Requires authenticationScopeNo additional permission

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/settings" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The tenant’s effective settings.
NameTypeConstraints
general.namerequired
string1–200 chars
general.timezonerequired
string1–100 chars
general.currencyrequired
string3–3 chars
general.defaultLocalerequired
enum: "ar" | "en" | "he"
general.logoFileIdrequired
string (uuid) | null
general.contactEmailrequired
string (email) | null0–320 chars
general.contactPhonerequired
string | null1–50 chars
general.addressrequired
string | null1–500 chars
attendance.lateThresholdMinutesrequired
integer0–120
attendance.notifyGuardianOnAbsencerequired
boolean
grading.scalerequired
enum: "percentage" | "letter" | "custom"
assessment.autoSendOnPublishrequired
boolean
billing.invoiceDueDaysrequired
integer0–365
billing.allowOverpaymentrequired
boolean
billing.invoicePrefixrequired
string1–10 chars
billing.autoInvoiceOnEnrollmentrequired
boolean
notifications.smsEnabledrequired
boolean
notifications.digestHourLocalrequired
integer0–23
notifications.channelLadderrequired
array<enum: "push" | "whatsapp" | "email" | "sms" | "in_app">
curriculum.autoCompleteThresholdPercentrequired
integer50–100
curriculum.notifyOnLessonPublishedrequired
boolean
curriculum.teraboxEnabledrequired
boolean
updates.autoUpdaterequired
boolean
updates.channelrequired
enum: "stable" | "beta"
integration.publicCatalogueEnabledrequired
boolean
integration.captchaSiteKeyrequired
string | null1–200 chars
integration.registrationsToLeadsrequired
boolean
json
{
  "general.name": "string",
  "general.timezone": "string",
  "general.currency": "USD",
  "general.defaultLocale": "ar",
  "general.logoFileId": "5987666b-5987-7987-8876-5987666b5987",
  "general.contactEmail": "ada.lovelace@example.com",
  "general.contactPhone": "string",
  "general.address": "string",
  "attendance.lateThresholdMinutes": 0,
  "attendance.notifyGuardianOnAbsence": true,
  "grading.scale": "percentage",
  "assessment.autoSendOnPublish": true,
  "billing.invoiceDueDays": 0,
  "billing.allowOverpayment": true,
  "billing.invoicePrefix": "string",
  "billing.autoInvoiceOnEnrollment": true,
  "notifications.smsEnabled": true,
  "notifications.digestHourLocal": 0,
  "notifications.channelLadder": [
    "push"
  ],
  "curriculum.autoCompleteThresholdPercent": 50,
  "curriculum.notifyOnLessonPublished": true,
  "curriculum.teraboxEnabled": true,
  "updates.autoUpdate": true,
  "updates.channel": "stable",
  "integration.publicCatalogueEnabled": true,
  "integration.captchaSiteKey": "string",
  "integration.registrationsToLeads": true
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

PATCH /api/v1/settings

Update one or more settings. An unknown key, or an invalid value, is rejected (422).

Requires authenticationScopesettings.manage

Request body

None.

Example

json
{}

curl example

curl
curl -X PATCH "https://api.yourdomain.com/api/v1/settings" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'

Responses

200The tenant’s new effective settings.
NameTypeConstraints
general.namerequired
string1–200 chars
general.timezonerequired
string1–100 chars
general.currencyrequired
string3–3 chars
general.defaultLocalerequired
enum: "ar" | "en" | "he"
general.logoFileIdrequired
string (uuid) | null
general.contactEmailrequired
string (email) | null0–320 chars
general.contactPhonerequired
string | null1–50 chars
general.addressrequired
string | null1–500 chars
attendance.lateThresholdMinutesrequired
integer0–120
attendance.notifyGuardianOnAbsencerequired
boolean
grading.scalerequired
enum: "percentage" | "letter" | "custom"
assessment.autoSendOnPublishrequired
boolean
billing.invoiceDueDaysrequired
integer0–365
billing.allowOverpaymentrequired
boolean
billing.invoicePrefixrequired
string1–10 chars
billing.autoInvoiceOnEnrollmentrequired
boolean
notifications.smsEnabledrequired
boolean
notifications.digestHourLocalrequired
integer0–23
notifications.channelLadderrequired
array<enum: "push" | "whatsapp" | "email" | "sms" | "in_app">
curriculum.autoCompleteThresholdPercentrequired
integer50–100
curriculum.notifyOnLessonPublishedrequired
boolean
curriculum.teraboxEnabledrequired
boolean
updates.autoUpdaterequired
boolean
updates.channelrequired
enum: "stable" | "beta"
integration.publicCatalogueEnabledrequired
boolean
integration.captchaSiteKeyrequired
string | null1–200 chars
integration.registrationsToLeadsrequired
boolean
json
{
  "general.name": "string",
  "general.timezone": "string",
  "general.currency": "USD",
  "general.defaultLocale": "ar",
  "general.logoFileId": "5987666b-5987-7987-8876-5987666b5987",
  "general.contactEmail": "ada.lovelace@example.com",
  "general.contactPhone": "string",
  "general.address": "string",
  "attendance.lateThresholdMinutes": 0,
  "attendance.notifyGuardianOnAbsence": true,
  "grading.scale": "percentage",
  "assessment.autoSendOnPublish": true,
  "billing.invoiceDueDays": 0,
  "billing.allowOverpayment": true,
  "billing.invoicePrefix": "string",
  "billing.autoInvoiceOnEnrollment": true,
  "notifications.smsEnabled": true,
  "notifications.digestHourLocal": 0,
  "notifications.channelLadder": [
    "push"
  ],
  "curriculum.autoCompleteThresholdPercent": 50,
  "curriculum.notifyOnLessonPublished": true,
  "curriculum.teraboxEnabled": true,
  "updates.autoUpdate": true,
  "updates.channel": "stable",
  "integration.publicCatalogueEnabled": true,
  "integration.captchaSiteKey": "string",
  "integration.registrationsToLeads": true
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/subscription

This tenant’s subscription: plan, status, current period, lock timestamp, and plan limits. Always readable, even while locked.

Requires authenticationScopeNo additional permission

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/subscription" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The tenant’s subscription.
NameTypeConstraints
planrequired
object
statusrequired
enum: "trialing" | "active" | "expired" | "suspended" | "cancelled"
currentPeriodStartrequired
string (date-time)
currentPeriodEndrequired
string (date-time)
lockedAtrequired
string (date-time) | null
limitsrequired
PlanLimits
json
{
  "plan": {
    "id": "37386ae0-3738-7738-8386-37386ae03738",
    "code": "string",
    "name": "Algebra I"
  },
  "status": "trialing",
  "currentPeriodStart": "2026-03-02T09:00:00.000Z",
  "currentPeriodEnd": "2026-03-02T09:00:00.000Z",
  "lockedAt": "2026-03-02T09:00:00.000Z",
  "limits": {
    "maxStudents": -9007199254740991,
    "maxBranches": -9007199254740991,
    "maxVideoCourses": -9007199254740991,
    "maxLessonsPerCourse": -9007199254740991,
    "notificationQuota": {
      "whatsapp": -9007199254740991,
      "sms": -9007199254740991,
      "email": "ada.lovelace@example.com"
    }
  }
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.