Skip to content

Provider console API

The provider console has an API of its own, separate from the customer API in every way: a different listener, a different base path, a different session cookie, and no API keys. The console’s web app is built on it. It exists only on a hosted installation with the console switched on.

Listener SP_PROVIDER_ADDR, never the customer port
Base path /provider/api/v1
Authentication A staff session cookie, set by signing in. No API keys: any request with an Authorization header is answered 401
OpenAPI description GET /provider/api/v1/openapi.json (and .yaml). There is no interactive documentation page
Scope One region. On a multi-region service each region’s console answers for its own workspaces

Nothing in this API returns what a workspace holds: no customers, transactions, alerts, cases or reports.

Endpoint Does Answers
POST /auth/login Signs in with email, password and, once enrolled, totp_code (six digits) 200 with the session and a Set-Cookie; 401 with invalid_credentials (wrong details or a wrong code), totp_required (send the code) or temporary_password_expired; 429 when rate limited
GET /auth/session The signed-in member of staff 200
POST /auth/logout Signs out 204 and a cookie that clears the session
POST /auth/password Changes the password (current, new) and signs out the other sessions 204; 400 if current is wrong; 422 if the new one is too short or the same
POST /auth/totp/setup Starts second-factor enrolment, after the temporary password was changed. Answers the secret once 200; 403 while the temporary password is unchanged; 409 if already set up
POST /auth/totp/verify Finishes enrolment with a code 200 with the session; 400 for a wrong code; 429 after 10 tries in 15 minutes

The session object:

Field Meaning
staff_id, email, name, role Who is signed in. role is owner, operations, support, billing or viewer
permissions What the role may do
mfa The second factor was verified for this session
must_change_password, mfa_enrol_required What still has to be done before the account works
restricted true until both are done: only the sign-in endpoints work
csrf_token Send it as X-CSRF-Token on every request that is not a GET
idle_timeout_seconds, expires_at The idle timeout, and the session’s absolute end
  • Requests that change something must come from the console’s own origin (the Origin or Sec-Fetch-Site header is checked) and carry the session’s X-CSRF-Token. Otherwise 403. Signing in is the one exception to the token.
  • A session that is restricted gets 403 complete account setup first (change the temporary password, then enrol two-factor) everywhere else.
  • A role without the permission gets 403 your role does not allow this (needs …). The check runs before the request body is read.
  • From outside SP_PROVIDER_ALLOWED_CIDRS, everything but /healthz is 403 not allowed from this network.
  • Request bodies are limited to 1 MB. Nothing is cached (Cache-Control: no-store).
Status Meaning
401 Not signed in, or the session ended
403 The role doesn’t allow it, the account isn’t set up, dual control forbids it, or a workspace’s policy refuses a support request
404 No such workspace, request, notice or member of staff
409 The state doesn’t allow it (already decided, already scheduled, last owner, your own account)
422 The request itself is wrong
429 Too many attempts
Endpoint Does Needs
GET /system/info The build, the region, the environment, whether scheduled deletions run by themselves (purge_enabled), and what each role may do Any member of staff
Endpoint Does Needs
GET /workspaces Lists workspaces. Query: search, status (active, suspended, closed), plan, limit (up to 100, default 50), offset. Also answers total, the plans in use and how many were deleted workspaces.view
POST /workspaces Creates a workspace and its first administrator. Answers 201 with workspace_id, admin_email and a temp_password shown once workspaces.create
GET /workspaces/deleted What is kept of deleted workspaces workspaces.view
GET /workspaces/{id} A workspace’s summary workspaces.view
GET /workspaces/{id}/health Health in numbers workspaces.view
GET /workspaces/{id}/usage Usage per calendar month, and AI assistant calls and tokens. Query: months, 1 to 24, default 6 usage.view
GET /workspaces/{id}/plan The plan, limits and staff notes plans.view
PUT /workspaces/{id}/plan Changes them plans.manage
POST /workspaces/{id}/ai-topups Adds AI assistant calls for the current calendar month, unasked or in answer to a request plans.manage
POST /workspaces/{id}/ai-requests/{request_id}/decline Declines a workspace’s request for more AI calls. Answers 204 plans.manage

Fields of a workspace in the list:

Field Meaning
id, slug, name, kind, country, status, created_at The workspace
region, move_state Its region, and a move in progress
plan The plan’s name
user_count People who can sign in (active and invited)
last_activity_at When someone last signed in
delete_after Set when deletion is scheduled
over_limit Over an agreed limit: people, or transactions this calendar month
ai_request_pending true when the workspace has asked for more AI assistant calls this month and nobody has answered; left out otherwise. An expired request from an earlier month doesn’t count
events_percent, users_percent Usage as a percentage of each agreed number, rounded down; absent when none was agreed

GET /workspaces/{id} adds base_currency, timezone, jurisdiction, active_user_count, suspended_at, suspended_reason, deletion_requested_at and, when deletion is scheduled, deletion_state (scheduled, due, awaiting_manual_offboard or blocked) and deletion_blocked_by (not_suspended, moving, legal_hold, no_dual_control or reports_not_exported). See Scheduled deletion.

A plan:

{
"plan": "standard",
"limit_monthly_events": 2000000,
"limit_users": 25,
"limit_monthly_ai_calls": 500,
"notes": "Renewal in March."
}

Leave a limit out for “none agreed”. limit_monthly_ai_calls is the workspace’s monthly allowance of AI assistant calls on the installation’s key, and unlike the other two it is enforced: left out, the installation’s default (SP_AI_MONTHLY_CALLS) applies; 0 means the installation’s key is not in the plan. PUT replaces the whole plan, so a request that leaves the field out clears an agreed allowance. See Plans and limits.

GET /workspaces/{id}/usage also answers limit_monthly_ai_calls and ai: one object per month, model and key, with month (2026-10), model, key_source (installation, billable, or own, the workspace’s own key), calls, input_tokens and output_tokens, added up over what the assistant was asked to do. Counts only.

It also answers what the More AI calls panel shows:

Field Meaning
ai_requests The workspace’s latest requests for more calls, newest first, at most 20: id, month (the month it was asked in), calls, note (the workspace’s words), status (pending, granted, declined, or expired for one left unanswered when its month ended; worked out when read, nothing is written), and once answered granted_calls, decided_by (the member of staff), decided_at and decision_note. Never who in the workspace asked
ai_topups Calls added for the months shown, newest first: id, month, calls, note (staff’s), request_id when it answered a request, granted_by, created_at
ai_topup_this_month The calls added for the current calendar month, on top of the agreed allowance

POST /workspaces/{id}/ai-topups adds calls to the workspace’s allowance on the installation’s key for the current calendar month (UTC). They are available at once and do not carry over.

{
"calls": 200,
"note": "Order 2026-115",
"request_id": "01a133e9-4c63-74a3-9e24-3522780eb136"
}
Field Rule
calls 1 to 1,000,000
note Optional, up to 1,000 characters. With request_id it becomes the answer shown to the workspace; without, it stays in the console
request_id Optional. The pending request this answers; it is marked granted with calls as the number granted

It answers 200 with the top-up (id, month, calls, note, request_id, granted_by, created_at). POST /workspaces/{id}/ai-requests/{request_id}/decline takes {"note": "…"} (optional, shown to the workspace) and answers 204.

Status When
403 your role does not allow this (needs plans.manage)
404 No such workspace, or no such request for this workspace
409 this request has already been answered; on a top-up also this request was for an earlier month and has expired; add the calls without it (a request_id whose month has ended), this workspace’s plan does not include the assistant on the installation’s key; change the plan before adding calls (the plan’s limit_monthly_ai_calls is 0) and this workspace is closed
422 calls must be between 1 and 1,000,000, or the note can be at most 1000 characters

Both write to the console’s audit log (workspace.ai_topup, workspace.ai_request_declined) and to the workspace’s (provider.ai_topup, provider.ai_request_declined), and notify the workspace’s administrators. Nothing checks that the workspace has a limit to add to: see Adding calls for a month.

Creating a workspace takes slug, name, kind (psp, fintech, bank, mobile_money, microfinance, other), admin_email, and optionally country, base_currency, timezone, jurisdiction, admin_name and plan. plan is an object like the one above, not a name; inside it plan and notes must be present (notes may be empty), as in PUT /workspaces/{id}/plan. A warning in the answer means the workspace exists but something around it needs attention, such as the global directory not listing it yet.

Suspending, reactivating, scheduling a deletion and cancelling one take two people. See Suspending and deleting a workspace.

Endpoint Does Needs
GET /changes Lists requests. Query: status (pending, approved, rejected, cancelled, expired, or decided for everything no longer pending), workspace_id, mine, limit (up to 200), offset changes.view
POST /changes Proposes a change. Answers 201. Changes nothing about the workspace changes.request
GET /changes/{id} One request changes.view
POST /changes/{id}/approve Approves and applies it. Optional note. 403 for whoever proposed it changes.approve
POST /changes/{id}/reject Rejects it. Optional note. 403 for whoever proposed it changes.approve
POST /changes/{id}/cancel Withdraws your own request changes.request
{
"kind": "workspace.schedule_deletion",
"workspace_id": "0198c1de-0000-7000-8000-000000000000",
"reason": "Contract ended on 30 September; export handed over.",
"deletion_days": 30
}

kind is workspace.suspend, workspace.reactivate, workspace.schedule_deletion or workspace.cancel_deletion. reason is 10 to 2,000 characters. deletion_days is 30 to 3650 and applies only to scheduling a deletion. A request lapses 72 hours after it is made (expires_at).

Endpoint Does Needs
GET /notices Lists notices. Query: active (only those in force now), limit, offset notices.view
POST /notices Posts one. Answers 201 notices.manage
PUT /notices/{id} Changes its text, severity and dates. Its audience is fixed. 409 if it was withdrawn notices.manage
POST /notices/{id}/withdraw Takes it down at once notices.manage
Field Meaning
severity info, warning or critical
title_en, body_en Required. Up to 200 and 4,000 characters
title_fr, body_fr Both or neither. French falls back to English
starts_at, ends_at Default: now, and until withdrawn
workspace_id On create only. Absent: every workspace

A workspace’s people read the notices addressed to them through the customer API, GET /api/v1/notices. See Service notices.

Owners only (staff.manage), except the names list.

Endpoint Does
GET /staff Lists staff: email, name, role, status (invited, active, disabled), whether the second factor is enrolled, last sign-in, and any lockout
GET /staff/names Names and status only, for filters. Open to every member of staff and not recorded in the audit log
POST /staff Invites someone (email, name, role). Answers 201 with a temp_password shown once
PUT /staff/{id}/role Changes a role. Not your own, and not the last active owner’s
POST /staff/{id}/deactivate Deactivates and ends their sessions
POST /staff/{id}/reactivate Lets them sign in again
POST /staff/{id}/reset-totp Removes their second factor, ends their sessions and answers a new temp_password
POST /staff/{id}/reset-password Ends their sessions and answers a new temp_password. The second factor stays

Every temporary password answer carries temp_password_valid_hours.

Endpoint Does Needs
GET /audit The console’s audit log, newest first audit.view
Query Meaning
action An action or its prefix, such as workspace. or auth.login_failed
workspace_id, staff_id Narrow to one
before, before_id Page backwards: the at and id of the last entry seen
after, after_id Page forwards: the entries that follow the newest one seen. Not together with before
limit Up to 200, default 100

Each entry has id, at, staff_id, staff_email (cli for the command line), action, workspace_id, workspace_slug, details, ip and request_id. Reading the log writes an audit.view entry. See The console’s audit log for every action.

Endpoint Does Needs
POST /workspaces/{id}/support-requests Asks a workspace for support access. Answers 201. Opens nothing support.request
GET /support-requests Lists requests. Query: status, workspace_id, limit, offset support.view
GET /support-requests/{id} One request support.view
POST /support-requests/{id}/cancel Withdraws your own pending request support.request
POST /support-requests/{id}/session Answers a one-time url into the customer app and when the link stops working (expires_at). Only for whoever made the request, while the grant runs support.request
POST /support-requests/{id}/end Ends a running grant. Whoever asked for it, or an owner support.request
{
"reason": "Webhook deliveries to the core banking system fail since Tuesday; need to see the endpoint settings.",
"ticket_ref": "SUP-4821",
"scope": "configuration",
"duration_minutes": 60
}

reason is 20 to 2,000 characters. scope is configuration (the default) or full_read. duration_minutes is 15 to 480, default 60. A request is refused with 403 when the workspace’s policy doesn’t take requests, doesn’t allow the scope, or allows less time.

A request’s status is pending, approved, denied, cancelled, expired, revoked or ended; granted_scope and granted_minutes say what the workspace gave, which can be narrower than what was asked; active is true while a session can be opened. See Support access.