Skip to content

API Reference — Batch 7

This batch documents the remaining undocumented API endpoint groups: WebAuthn/FIDO2 hardware key authentication, conversation tags, IP allowlist admin, SCIM token management, and conversation search.

Base URL: https://api.arbitex.example.com

Authentication: All endpoints require Authorization: Bearer <token> unless noted. Admin endpoints require an admin-scoped token.


Hardware security key and passkey registration, authentication, and management. Routes are in backend/app/api/webauthn.py.

Router prefixes:

  • User endpoints: /api/auth/webauthn
  • Admin endpoint: /api/admin

Start the WebAuthn credential registration ceremony for the currently authenticated user.

Returns PublicKeyCredentialCreationOptions for use with navigator.credentials.create(). The challenge is stored server-side and verified in the complete step.

Auth: Bearer token (user)

Response 200 OK:

{
"options": {
"challenge": "<base64url>",
"rp": {
"name": "Arbitex",
"id": "api.arbitex.example.com"
},
"user": {
"id": "<base64url-user-id>",
"name": "user@example.com",
"displayName": "User Name"
},
"pubKeyCredParams": [
{ "type": "public-key", "alg": -7 },
{ "type": "public-key", "alg": -257 }
],
"timeout": 60000,
"attestation": "none",
"authenticatorSelection": {
"userVerification": "preferred"
}
}
}

Complete the WebAuthn registration ceremony. Verifies the attestation response from navigator.credentials.create() and stores the credential.

Auth: Bearer token (user)

Request body:

{
"id": "<credential-id>",
"rawId": "<base64url>",
"response": {
"clientDataJSON": "<base64url>",
"attestationObject": "<base64url>"
},
"type": "public-key"
}

Response 201 Created:

{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"credential_id": "<base64url-credential-id>",
"name": "Security Key",
"created_at": "2026-03-12T21:00:00Z",
"last_used_at": null
}

POST /api/auth/webauthn/authenticate/begin

Section titled “POST /api/auth/webauthn/authenticate/begin”

Start the WebAuthn authentication ceremony. Returns assertion options for use with navigator.credentials.get().

Auth: Public (no token required)

Request body:

{
"email": "user@example.com"
}

Response 200 OK:

{
"options": {
"challenge": "<base64url>",
"timeout": 60000,
"rpId": "api.arbitex.example.com",
"allowCredentials": [
{
"type": "public-key",
"id": "<base64url-credential-id>",
"transports": ["usb", "nfc"]
}
],
"userVerification": "preferred"
}
}

POST /api/auth/webauthn/authenticate/complete

Section titled “POST /api/auth/webauthn/authenticate/complete”

Complete the WebAuthn authentication ceremony. Verifies the assertion from navigator.credentials.get() and issues JWT access and refresh tokens.

Auth: Public (no token required)

Request body:

{
"email": "user@example.com",
"id": "<credential-id>",
"rawId": "<base64url>",
"response": {
"clientDataJSON": "<base64url>",
"authenticatorData": "<base64url>",
"signature": "<base64url>",
"userHandle": "<base64url>"
},
"type": "public-key"
}

Response 200 OK:

{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"token_type": "bearer"
}

The issued access token carries mfa_verified: true when the user’s org has MFA enforcement enabled and WebAuthn satisfies the MFA requirement. See MFA Enforcement.


List all WebAuthn credentials registered by the current user.

Auth: Bearer token (user)

Response 200 OK:

{
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"credential_id": "<base64url>",
"name": "YubiKey 5C",
"created_at": "2026-02-10T09:00:00Z",
"last_used_at": "2026-03-12T08:30:00Z"
}
],
"total": 1
}

DELETE /api/auth/webauthn/credentials/{credential_id}

Section titled “DELETE /api/auth/webauthn/credentials/{credential_id}”

Delete a registered WebAuthn credential. Users can only delete their own credentials.

Auth: Bearer token (user)

Path parameter: credential_id — credential record UUID

Response 200 OK:

{
"detail": "Credential deleted"
}

Errors: 404 Not Found if the credential does not exist or belongs to another user.


DELETE /api/admin/users/{user_id}/webauthn

Section titled “DELETE /api/admin/users/{user_id}/webauthn”

Revoke all WebAuthn credentials for a user. Admin only. Used to recover access when a user loses all hardware keys.

Auth: Bearer token (admin)

Path parameter: user_id — target user UUID

Response 200 OK:

{
"revoked": 2
}

Notes: This permanently removes all credentials. The user will need to re-register hardware keys on next login. Also see MFA Enforcement: Admin MFA Management.


User-scoped tagging for conversations — create, list, update, delete tags and assign/remove them from conversations. Routes are in backend/app/api/tags.py.

Router prefix: /api/tags


Create a new tag for the current user.

Auth: Bearer token (user)

Request body:

{
"name": "work-projects",
"color": "#3b82f6"
}
FieldRequiredTypeNotes
nameYesstringTag label
colorNostringHex color code for UI display

Response 201 Created:

{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "work-projects",
"color": "#3b82f6",
"created_at": "2026-03-12T21:00:00Z"
}

List all tags belonging to the current user.

Auth: Bearer token (user)

Response 200 OK:

[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "work-projects",
"color": "#3b82f6",
"created_at": "2026-03-12T21:00:00Z"
}
]

Get a single tag by ID. Only returns the tag if it belongs to the current user.

Auth: Bearer token (user)

Response 200 OK: Tag object (same as create response).

Errors: 404 Not Found if the tag does not exist or belongs to another user.


Update a tag’s name or color. Both fields are optional.

Auth: Bearer token (user)

Request body:

{
"name": "personal-projects",
"color": "#10b981"
}

Response 200 OK: Updated tag object.

Errors: 404 Not Found if the tag does not exist.


Delete a tag. Also removes all conversation-tag associations for this tag.

Auth: Bearer token (user)

Response 204 No Content

Errors: 404 Not Found if the tag does not exist.


POST /api/tags/conversations/{conversation_id}/assign

Section titled “POST /api/tags/conversations/{conversation_id}/assign”

Assign a tag to a conversation.

Auth: Bearer token (user)

Path parameter: conversation_id — conversation UUID

Request body:

{
"tag_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

Response 201 Created:

{
"conversation_id": "abc12345-...",
"tag_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

Errors:

  • 404 Not Found — conversation or tag not found
  • 409 Conflict — tag is already assigned to this conversation

DELETE /api/tags/conversations/{conversation_id}/tags/{tag_id}

Section titled “DELETE /api/tags/conversations/{conversation_id}/tags/{tag_id}”

Remove a tag from a conversation.

Auth: Bearer token (user)

Response 204 No Content

Errors: 404 Not Found if the tag assignment does not exist.


CRUD management of IP allowlist entries. When the allowlist is non-empty, requests from IP addresses not matching any active CIDR range are blocked by the IP allowlist middleware. Routes are in backend/app/api/ip_allowlist.py.

Router prefix: /api/admin/ip-allowlist

All endpoints require admin authentication. Changes take effect immediately — the middleware cache is invalidated on every create, update, and delete.


List all IP allowlist entries, ordered by creation date (newest first).

Auth: Bearer token (admin)

Response 200 OK:

[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"ip_range": "10.0.0.0/8",
"description": "Internal network",
"tenant_id": null,
"is_active": true,
"created_by_id": "user-uuid",
"created_at": "2026-03-01T10:00:00Z"
}
]
FieldTypeDescription
ip_rangestringCIDR notation (e.g. 10.0.0.0/8, 203.0.113.0/24)
descriptionstringHuman-readable note
tenant_idstring|nullScope to a specific tenant org UUID, or null for global
is_activebooleanIf false, entry exists but is not enforced
created_by_idstringAdmin user who created the entry

Get a single IP allowlist entry by ID.

Auth: Bearer token (admin)

Response 200 OK: Entry object (same schema as list).

Errors: 404 Not Found if the entry does not exist.


Create a new IP allowlist entry.

Auth: Bearer token (admin)

Request body:

{
"ip_range": "203.0.113.0/24",
"description": "Office network",
"tenant_id": null,
"is_active": true
}

Response 201 Created: The created entry object.

Notes: The CIDR range is validated by the schema layer (must be valid IPv4 or IPv6 CIDR notation). The middleware cache is cleared on success so the new entry takes effect without delay.


Update an existing IP allowlist entry. All fields are optional (partial update).

Auth: Bearer token (admin)

Request body:

{
"description": "Updated description",
"is_active": false
}

Response 200 OK: Updated entry object.

Notes: The middleware cache is cleared on success.

Errors: 404 Not Found if the entry does not exist.


Delete an IP allowlist entry. The change takes effect immediately.

Auth: Bearer token (admin)

Response 204 No Content

Errors: 404 Not Found if the entry does not exist.


Per-org SCIM bearer token lifecycle: creation, listing, and revocation. Tokens are stored as SHA-256 hashes — the plaintext is returned only once at creation. Routes are in backend/app/api/scim_tokens.py.

Router prefix: /api/admin/scim/tokens

SCIM token management requires admin authentication. See SCIM Provisioning for using SCIM tokens to provision users and groups.


Create a new SCIM bearer token for an organisation. The plaintext token is returned once and cannot be recovered — store it securely.

Auth: Bearer token (admin)

Request body:

{
"name": "Okta SCIM Integration",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
FieldRequiredTypeNotes
nameNostringHuman-readable label (e.g. “Okta”, “Azure AD”)
org_idYesUUIDOrganisation to create the token for

Response 201 Created:

{
"id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Okta SCIM Integration",
"token": "scim_01HXYZ....",
"created_at": "2026-03-12T21:00:00Z"
}

Security: Copy the token value immediately. It is not stored in plaintext and cannot be retrieved after this response.


List all SCIM tokens for the admin’s organisation. Does not return plaintext tokens.

Auth: Bearer token (admin)

Response 200 OK:

[
{
"id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Okta SCIM Integration",
"created_at": "2026-03-12T21:00:00Z"
}
]

Revoke a SCIM token. The token is immediately invalidated — any SCIM requests using it will fail authentication.

Auth: Bearer token (admin)

Path parameter: token_id — token record UUID

Response 204 No Content


Full-text search across the current user’s conversation titles and message content. Routes are in backend/app/api/search.py.


Search conversations by title and message content. Case-insensitive. Returns paginated results with matching snippets.

Auth: Bearer token (user)

Query parameters:

ParameterRequiredTypeDefaultDescription
qYesstringSearch query (1–500 characters)
pageNointeger1Page number (1-based)
per_pageNointeger20Results per page (1–100)
date_fromNoISO-8601 datetimenullFilter: conversations created on or after this date
date_toNoISO-8601 datetimenullFilter: conversations created on or before this date

Example request:

GET /api/search/conversations?q=kubernetes+deployment&page=1&per_page=20

Response 200 OK:

{
"items": [
{
"id": "conv-uuid",
"title": "Kubernetes Deployment Troubleshooting",
"snippet": "...the kubernetes deployment failed because the image pull policy...",
"created_at": "2026-03-10T14:30:00Z",
"updated_at": "2026-03-10T15:00:00Z",
"mode": "single"
}
],
"total": 4,
"page": 1,
"per_page": 20,
"pages": 1
}
FieldTypeDescription
itemsarrayMatching conversation results
items[].snippetstringExcerpt of matching message content
totalintegerTotal number of matching conversations
pageintegerCurrent page
per_pageintegerResults per page
pagesintegerTotal pages

Notes:

  • Search is scoped to the authenticated user’s own conversations only.
  • Archived conversations are included in results.
  • The snippet field contains the matching text excerpt with the query term in context.