# Langsys API Documentation (v1.0.0) > This is the complete Langsys API reference in LLM-friendly markdown format. ## Authentication ### bearerAuth Enter token in format (Bearer ) - Type: http - Scheme: bearer - Name: Authorization - In: header ### apiKey An ApiKey security string - Type: apiKey - Scheme: bearer - Name: X-Authorization - In: header ## API Key ### GET /api/api-keys/{apiKey}/app-identities **List app identities bound to an API key** The app binaries this key trusts for device attestation. A Play Integrity or App Attest verdict only opens the write gate when its attested identity matches one of these rows. **Parameters:** - `apiKey` (path): string (required) **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/api-keys/{apiKey}/app-identities **Register an app identity on an API key** Registers a mobile app binary this key should trust for device attestation. Android: package name plus signing-certificate SHA-256 digests (several allowed — debug + release, or both during a certificate rotation). iOS: TEAMID.bundle.id. **Parameters:** - `apiKey` (path): string (required) **Request Body:** Content-Type: application/json - `platform`: string (required) — Which platform this identity describes. - `identifier`: string (required) — Android: the application's package name. iOS: TEAMID.bundle.id — the App ID attestation is bound to. - `certificate_sha256_digests`: string[] — Android only: allowed signing-certificate SHA-256 digests, base64url-encoded as Play Integrity reports them. Register several to cover debug + release certificates, or both certificates during a key rotation. Ignored for iOS. **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### DELETE /api/api-keys/{apiKey}/app-identities/{appIdentity} **Remove an app identity from an API key** Attestations from this binary stop opening the write gate immediately. Any App Attest keys registered through it are revoked with it. **Parameters:** - `apiKey` (path): string (required) - `appIdentity` (path): string (required) **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/api-keys **List API Keys** Get all API keys the authenticated user has access to. Optionally filter by organization_id or project_id. organization_id will be ignored if project_id is provided as well. User must be an admin to access the api keys of a project or organization. Results can be ordered by: name, description, type, active, last_used_at, created_at, updated_at, id. **Parameters:** - `organization_id` (query): string — Filter results by organization ID - `project_id` (query): string — Filter results by project ID - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `active`, `description`, `last_used_at`, `name`, `type` **Default order:** `name:asc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `active`, `name`, `type` **Default filters:** `active:true` **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/api-keys **Create a new API Key** Create a new API key and associate it with a project. **Request Body:** Content-Type: application/json - `project_id`: string (required) — The project ID to associate the API key with. - `name`: string (required) — The name of the API key. - `description`: string - `type`: "read" | "write" | "ip_write" — The type of the API key. 'ip_write' is read-only except from the IPs in ip_allowlist. - `active`: boolean — Whether the API key is active. - `ip_allowlist`: string[] — IPs/CIDRs that unlock write for an 'ip_write' key (e.g. ["203.0.113.0/24"]). Ignored for read/write keys. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `ip_allowlist`.`invalid_format`: Each IP allowlist entry must be a valid IP address or CIDR range that isn't open (/0), with at most {max} entries. - 429: ### DELETE /api/api-keys/{apiKeyId} **Delete API Key** Delete an api key. **Parameters:** - `apiKeyId` (path): string (required) — Id of API Key **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### PATCH /api/api-keys/{apiKeyId} **Update API Key** Update the project assigned to an API key. **Parameters:** - `apiKeyId` (path): string (required) — Id of API Key **Request Body:** Content-Type: application/json - `name`: string — The name of the API key. - `description`: string - `type`: "read" | "write" | "ip_write" — The type of the API key. 'ip_write' is read-only except from the IPs in ip_allowlist. - `active`: boolean — Whether the API key is active. - `ip_allowlist`: string[] — IPs/CIDRs that unlock write for an 'ip_write' key (e.g. ["203.0.113.0/24"]). - `project_id`: string **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `ip_allowlist`.`invalid_format`: Each IP allowlist entry must be a valid IP address or CIDR range that isn't open (/0), with at most {max} entries. - 429: ### POST /api/api-keys/{apiKeyId}/write-grant/rotate **Rotate the write-grant secret** Generate or rotate the shared secret used to sign short-lived HS256 write-grant JWTs for this key (write-key gating design §6). The secret is returned once and cannot be retrieved again — store it in your auth server immediately. **Parameters:** - `apiKeyId` (path): string (required) — Id of API Key **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/api-keys/{apiKeyId}/write-grant **Remove the write-grant secret** Retire the write-grant lane for this key. Grants signed with the previous secret are refused immediately; the key keeps writing through its type and IP allow-list. **Parameters:** - `apiKeyId` (path): string (required) — Id of API Key **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## API Key - Reports ### GET /api/api-keys/{apiKeyId}/activity **Get activity history per day for an API key** Get per day activity for an specific API key **Parameters:** - `apiKeyId` (path): string (required) — Id of API Key - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/api-keys/{apiKeyId}/activity-summary **Get total activity history for an API key** Get total activity history for an API key. **Parameters:** - `apiKeyId` (path): string (required) — Id of API Key - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/api-keys/{apiKeyId}/usage **Get API usage units per day for an API key** Get per day API usage units for a specific API key **Parameters:** - `apiKeyId` (path): string (required) — Id of API Key - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/api-keys/{apiKeyId}/usage-summary **Get total API usage units for an API key** Get total API usage units for an API key within a date range **Parameters:** - `apiKeyId` (path): string (required) — Id of API Key - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## Auth ### POST /api/login (DEPRECATED) **Authenticate User (retired)** Password sign-in has been retired: this always answers 410 passwords_retired. Sign in with an emailed code (POST auth/email/send, then auth/email/verify) or a passkey (POST auth/passkey/options, then auth/passkey/verify). **Responses:** - 410: Passwords are retired - 429: ### POST /api/register **Register New User** Register a new user. An organization may be created as part of the registration process. Otherwise, it will need to be created later in order. There is no password: the account is created unverified and emailed a sign-in code and link, and `data.code_expires_at` says when they stop working. Verifying at POST auth/email/verify or auth/email/redeem then signs in and verifies the address. Sending a `password` (old app builds) answers 410 passwords_retired. **Request Body:** Content-Type: application/json - `firstname`: string (required) - `lastname`: string (required) - `email`: string (required) - `subscription_billing_address`: object — Required for paid plans. A free-plan sign-up may omit it; it is asked for when the plan is upgraded. - `plan_type`: "free" | "starter" | "pro" | "business" | "enterprise" - `plan_cycle`: "monthly" | "quarterly" | "biannual" | "yearly" | "lifetime" - `credit_card`: object - `organization`: object - `organization_name`: string - `organization_country`: string - `payment_method_type`: "credit_card" | "wire_transfer" - `auto_renew`: boolean - `password`: string — Retired. Omit it: sending a password answers 410 passwords_retired. - `password_confirmation`: string — Retired, like password. **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 402: Possible errors: - `no_valid_payment_method`: No default payment method found for the entity. - `payment_method_rejected`: This card was declined. Please use a different card or contact your bank. - 410: - 422: Possible errors: - `email_undeliverable`: The email address could not be verified. - `invalid_plan_cycle`: The requested plan type and billing cycle combination does not exist. - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `validation_failed`: The request failed validation. Possible validation errors: - `subscription_billing_address`.`required`: A billing address is required for paid plans. - `subscription_billing_address.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `subscription_billing_address.vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - `credit_card`.`required`: A credit card is required for paid plans. - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - `payment_method_type`.`not_allowed`: Wire transfer is only available for invited Enterprise plans. - 429: - 502: - 503: ### POST /api/register/{activationToken} **Register User with Invitation Token** Register a new user with an activation token. Registering with an activation token implies that this user will be added to an organization or project after creation. There is no password: the invitation link already proves the address, and a `password` sent by an old app build is ignored. The account always takes the invited address; a different `email` in the body is ignored. Send `device_id` to bind the returned session to your device; without it the session is bound to the legacy device id `Personal Access Token`. **Parameters:** - `activationToken` (path): string (required) — Activation token received by user through email. **Request Body:** Content-Type: application/json - `firstname`: string (required) - `lastname`: string (required) - `email`: string (required) - `subscription_billing_address`: object (required) - `payment_method_type`: "credit_card" | "wire_transfer" - `credit_card`: object - `organization`: object - `auto_renew`: boolean - `password`: string — Retired and ignored: the invitation link already proves the address. - `password_confirmation`: string — Retired and ignored. - `device_id`: string — Device the returned session is bound to; send it as x-device-id afterwards. Without it the session is bound to the legacy device id "Personal Access Token". - `device_info`: string[] — Optional description of the device. Recorded in the audit log only. - `remember_me`: boolean — Session lasts 3 months instead of 1 week. **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 402: - 404: - 422: Possible errors: - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `translator_limit_reached`: Your plan's translator limit is {limit}. - `user_limit_reached`: Your plan's user limit is {limit}. - `validation_failed`: The request failed validation. Possible validation errors: - `expired`: This invitation has expired. - `not_available`: This plan is no longer available for new subscriptions. - `not_available`: The invited plan has no available billing cycle. - `not_allowed`: Wire transfer is only available for Enterprise plans. - `not_allowed`: This plan has no fixed price. Please use wire transfer and contact support to arrange payment. - `required`: A payment method is required to subscribe to this plan. - `subscription_billing_address.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `subscription_billing_address.vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `email`.`already_taken`: The email has already been taken. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 502: - 503: ### GET /api/activate/{activationToken} **Activate User Account** If token is valid, account will be activated and user will be logged in. A bearer token will be available in the response, bound to the x-device-id header if sent, else to the legacy device id `Personal Access Token`. It expires after a week. **Parameters:** - `activationToken` (path): string (required) — Activation token received by user through email. **Responses:** - 200: Success - 404: - 429: ### GET /api/activate-email/{activationToken} **Verify New Email Address** If token is valid, the new email will be updated in the users table and deleted from the user_emails table. **Parameters:** - `activationToken` (path): string (required) — Activation token received by user through email. **Responses:** - 200: Success - 404: - 429: ### GET /api/authorize-project/{projectId} **Validate API Key for Project** Check if api key is valid for project. Api key should be sent as a X-Authorization header. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### POST /api/auth/logout **Sign Out** Revokes the session this request is made with. Other sessions stay signed in; list and revoke them with GET/DELETE me/sessions. **Responses:** - 204: Signed out - 401: - 403: - 429: ### POST /api/auth/email/send **Email a Sign-in Code** Emails a 6-digit code and a one-click sign-in link, both valid once for 10 minutes. Sending again replaces the previous code. Sign-in and sign-up are one flow: an address with no account gets the same email, and using its code creates the account (free plan, no name yet; see `created` on verify). The response is identical either way. An address with no account must pass the deliverability check first (422 email_undeliverable otherwise). Throttled to 3 per minute and 10 per hour per address, and 20 per hour per network (an IPv4 address, or an IPv6 /64), counting every send alike. **Request Body:** Content-Type: application/json - `email`: string (required) — Address to email the sign-in code and link to. **Responses:** - 202: Accepted - 422: Possible errors: - `email_undeliverable`: The email address could not be verified. - `validation_failed`: The request failed validation. - 429: ### POST /api/auth/email/verify **Sign In with an Emailed Code** Exchanges the emailed code for a session bound to device_id. Using the code also spends the link from the same email. Three wrong codes end the attempt. Every failure answers the same error, whatever the cause. `created` is true when this completed a sign-up: either the account was created just now (the address had none; it starts on the free plan with an empty firstname and lastname, which onboarding sets with PATCH me), or it was an unused account's first verification. **Request Body:** Content-Type: application/json - `email`: string (required) — The address the code was sent to. - `code`: string (required) — The 6-digit code from the email. - `device_id`: string (required) — Client-generated device identifier the session is bound to. Send it as the x-device-id header on every later request. - `device_info`: string[] — Optional description of the device (browser, OS, app version). Recorded in the audit log only. - `remember_me`: boolean — Session lasts 3 months instead of 1 week. **Responses:** - 200: Success - 422: Possible errors: - `email_code_invalid`: That code didn't work. Check it, or send a new one. - `validation_failed`: The request failed validation. - 429: ### POST /api/auth/email/redeem **Sign In with an Emailed Link** Exchanges the secret from the sign-in link (the part after `#`) for a session in whichever browser opened the link. The link page must POST it here; nothing signs in on a GET, so mail scanners that fetch the URL spend nothing. Using the link also spends the code from the same email. **Request Body:** Content-Type: application/json - `secret`: string (required) — The secret from the sign-in link, read from the URL fragment. - `device_id`: string (required) — Client-generated device identifier the session is bound to. Send it as the x-device-id header on every later request. - `device_info`: string[] — Optional description of the device (browser, OS, app version). Recorded in the audit log only. - `remember_me`: boolean — Session lasts 3 months instead of 1 week. **Responses:** - 200: Success - 422: Possible errors: - `email_code_invalid`: That code didn't work. Check it, or send a new one. - `validation_failed`: The request failed validation. - 429: ### GET /api/imagekit/auth **Generate ImageKit Authentication Token** Get a JWT and upload parameters for client-side avatar upload to ImageKit v2. **Responses:** - 200: Success - 401: - 403: - 429: ### POST /api/auth/native/redeem **Redeem a Native Handoff** The app exchanges the handoff code from the hosted page, plus the verifier whose SHA-256 it sent as code_challenge, for a session bound to the app's own device_id. Single use; the handoff lives 60 seconds. **Request Body:** Content-Type: application/json - `handoff`: string (required) — The handoff code the hosted page returned through the app's URL scheme. - `code_verifier`: string (required) — The verifier the app generated before opening the hosted page. - `device_id`: string (required) — Client-generated device identifier the session is bound to. Send it as the x-device-id header on every later request. - `device_info`: string[] — Optional description of the device (browser, OS, app version). Recorded in the audit log only. - `remember_me`: boolean — Session lasts 3 months instead of 1 week. **Responses:** - 200: Success - 422: Possible errors: - `handoff_invalid`: That sign-in has expired. Start again from the app. - `validation_failed`: The request failed validation. - 429: ### POST /api/auth/native/passkeys/options **Get Passkey Creation Options with a Ticket** The hosted page's equivalent of POST me/passkeys/options, authorised by the app's ticket instead of a session. Does not spend the ticket. **Request Body:** Content-Type: application/json - `ticket`: string (required) — The ticket the app minted with POST me/native-ticket and passed to the hosted page. **Responses:** - 200: Success - 422: Possible errors: - `native_ticket_invalid`: This page has expired. Start again from the app. - `validation_failed`: The request failed validation. - 429: ### POST /api/auth/native/passkeys **Add a Passkey with a Ticket** Spends the ticket and saves the passkey. The ticket is required again here, not just the challenge_id, so holding a challenge is never enough to attach an authenticator to someone's account. **Request Body:** Content-Type: application/json - `ticket`: string (required) — The ticket the app passed to the hosted page. Spent by this call. - `challenge_id`: string (required) — The challenge_id returned with the creation options. - `credential`: string[] (required) — The RegistrationResponseJSON from navigator.credentials.create(). - `name`: string — A name the user will recognise the passkey by. Defaults to "Passkey". **Responses:** - 201: Created - 422: Possible errors: - `native_ticket_invalid`: This page has expired. Start again from the app. - `passkey_verification_failed`: That passkey couldn't be verified. Try again, or sign in with an emailed code. - `validation_failed`: The request failed validation. - 429: ### POST /api/auth/passkey/options **Get Passkey Sign-in Options** Returns request options for navigator.credentials.get() and a challenge_id to send back with the result. The options name no credentials (allowCredentials is empty), so they reveal nothing about who has a passkey. **Responses:** - 200: Success - 429: ### POST /api/auth/passkey/verify **Sign In with a Passkey** Verifies the assertion. With device_id (web), returns a session bound to it. With code_challenge instead (the hosted page a native app opened), returns a single-use handoff code valid for 60 seconds, which the app redeems with its verifier at POST auth/native/redeem; no session is issued to the page. **Request Body:** Content-Type: application/json - `challenge_id`: string (required) — The challenge_id returned with the options. - `credential`: string[] (required) — The AuthenticationResponseJSON from navigator.credentials.get() (as @simplewebauthn/browser returns it). - `device_id`: string — Web sign-in: the device the session is bound to. Omit when sending code_challenge. - `device_info`: string[] — Optional description of the device. Recorded in the audit log only. - `remember_me`: boolean — Session lasts 3 months instead of 1 week. - `code_challenge`: string — Hosted native page: base64url SHA-256 of the app's verifier. Returns a handoff code instead of a session. **Responses:** - 200: A session (UserWithTokenResponse) or, with code_challenge, a handoff (HandoffResponse) - 422: Possible errors: - `passkey_verification_failed`: That passkey couldn't be verified. Try again, or sign in with an emailed code. - `validation_failed`: The request failed validation. Possible validation errors: - `device_id`.`required`: The device ID is required when code challenge is not present. - 429: ## Utilities ### GET /api/countries/{userLocale} **Get List of Countries in Specified Language** Get a complete list of all countries with the country name in {localeCode} as label attribute. Pagination parameters are optional. Results can be ordered by: label, code, created_at, updated_at, id. **Parameters:** - `userLocale` (path): string (required) — URL paramater representing locale code to display the data in. - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/countries/dial-codes/{userLocale} **Get List of Country Dial Codes in Specified Language** Get a complete list of all countries dial codes with the country name in {userLocale} as name attribute. Pagination parameters are optional. Results can be ordered by: country_code, dial_code, name, created_at, updated_at, id. **Parameters:** - `userLocale` (path): string (required) — URL paramater representing locale code to display the data in. - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/currencies/{userLocale} **Get List of Currencies in Specified Language** Get a complete list of all currencies with the currency name in {localeCode} as name attribute. Pagination parameters are optional. Results can be ordered by: name, code, symbol, decimal_digits, created_at, updated_at, id. **Parameters:** - `userLocale` (path): string (required) — URL paramater representing locale code to display the data in. - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction)

[View orderable fields and defaults](/documentation/ordering) - `filter_by` (query): string | string[] — Filter results by field values. Supports: equality (field:value), null check (field:null), non-null check (field:!null), comparison (field:>:value, field:<:value, field:>=:value, field:<=:value). Multiple filters: filter_by[]=field1:value&filter_by[]=field2:value

[View filterable fields and defaults](/documentation/filtering) **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/locales **Get a list of locales grouped by language** Get a list of locales grouped by language and displayed in the language of each requested locale. **Parameters:** - `locales[]` (query): string[] — List of locales to display the data in. Each locale in this list will be used to group data displayed in each locale's language. - `project_id` (query): string — If provided, the project's target locales will be appended to the list of locales. - `append_target_locales` (query): boolean — If project_id is not provided and this value is true, then the project's target locales will be appended to the list of locales. This is only when using api key authentication and project can be inferred from the api key **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `project_id`.`invalid_format`: The project ID must be a valid UUID. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/locales/flat **Get flat list of locales** Get a list of all locales. **Parameters:** - `locales[]` (query): string[] — List of locales to display the data in. Each locale in this list will be used to group data displayed in each locale's language. - `project_id` (query): string — If provided, the project's target locales will be appended to the list of locales. - `append_target_locales` (query): boolean — If project_id is not provided and this value is true, then the project's target locales will be appended to the list of locales. This is only when using api key authentication and project can be inferred from the api key **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `project_id`.`invalid_format`: The project ID must be a valid UUID. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/locales/data **Get detailed list of locales** Get a list of all locales with their names and language names. **Parameters:** - `locales[]` (query): string[] — List of locales to display the data in. Each locale in this list will be used to group data displayed in each locale's language. - `project_id` (query): string — If provided, the project's target locales will be appended to the list of locales. - `append_target_locales` (query): boolean — If project_id is not provided and this value is true, then the project's target locales will be appended to the list of locales. This is only when using api key authentication and project can be inferred from the api key **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `project_id`.`invalid_format`: The project ID must be a valid UUID. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/plans **List subscription plans** Retrieve all pricing rows (one per plan + billing cycle) whose parent plan is publicly live, plus Enterprise. Enterprise rows have `contact_sales: true`, no price and unlimited limits: they are requested, not bought. **Responses:** - 200: Success - 401: - 403: - 429: ## Organization - Coupons ### POST /api/organizations/{organizationId}/coupons/redeem **Redeem a Coupon** Redeem a coupon code to grant free credits to the organization's balance. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `code`: string (required) — The coupon code to redeem **Responses:** - 200: Coupon redeemed successfully - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `coupon_already_redeemed`: This coupon has already been redeemed by your organization. - `coupon_exhausted`: This coupon has reached its maximum number of redemptions. - `coupon_expired`: This coupon has expired. - `coupon_invalid`: Invalid coupon code. - `validation_failed`: The request failed validation. - 429: ## Organization - Credits Usage ### GET /api/organizations/{organizationId}/credits/usage-settings **Get Usage Monitoring Status** Get current usage monitoring status for an organization including confirmation state and MT disabled status. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/organizations/{organizationId}/credits/usage-settings/events **Get Credits Usage Confirmation Events** Get the history of credits usage confirmation events (confirmed, reset, resumed, auto_disabled) for an organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/organizations/{organizationId}/credits/usage-daily **Get Daily Credits Usage** Machine translation credits the organization used per day, organization-wide and per project, with today's spike verdict. Usage is recorded as each machine translation is charged, so today is current to the latest charge. Refunds for undone work are not subtracted. Days without usage are omitted. Usage of permanently removed projects stays in the organization totals but not in the per-project breakdown. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `start_date` (query): string — First day to include (YYYY-MM-DD, 2000-01-01 or later). Defaults to 29 days before end_date, a 30-day window. The window may span at most 90 days. - `end_date` (query): string — Last day to include (YYYY-MM-DD, 2000-01-01 or later). Defaults to today. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `start_date`.`invalid_date`: The start date must be on or before the end date. - `start_date`.`too_long`: The range from the start date to the end date may span at most 90 days. - 429: ### POST /api/organizations/{organizationId}/credits/usage/confirm **Confirm Elevated Usage** Confirm usage at 50% threshold to prevent auto-disable at 75%. Organization admin authorizes elevated usage to continue machine translation services. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/credits/usage/resume **Resume Machine Translation** Resume machine translation after it was auto-disabled at 75% usage spike. Organization admin re-enables MT service and confirms usage. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/credits/usage/reset **Reset Usage Confirmation** Reset usage confirmation so a future spike can trigger the confirmation prompt again. Organization admin calls this when their spike period is over and they want to be prompted again if usage spikes later. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## Deprecated ### GET /api/translations/{localeCode}/{projectId} **List Best Translations Categorized in Flat Format (Deprecated)** DEPRECATED: Use GET /api/projects/{projectId}/translations instead.

Get a list of translations. Response will be an object representing a list of translations in the form [Category] => [phrase => translation]. Translations follow the following order: [untranslated DESC, ai_translated DESC, memory_translated DESC, translatable DESC, phrase/label ASC]. **Parameters:** - `localeCode` (path): string (required) — Code of translation locale - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### POST /api/tokens/{projectId} **Create Project Phrases (Deprecated)** DEPRECATED: Use POST /api/projects/{projectId}/phrases instead.

Create a set of phrases for a project. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `tokens`: object[] (required) **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/locales/{userLocale} **Get Categorized List of Locales (Deprecated)** Get a list of all locales. **Parameters:** - `userLocale` (path): string (required) — URL paramater representing locale code to display the data in. **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/locales/{userLocale}/flat **Get Flat List of Locales (Deprecated)** Get a list of all locales. **Parameters:** - `userLocale` (path): string (required) — URL paramater representing locale code to display the data in. **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/locales/{userLocale}/data **Get Detailed List of Locales (Deprecated)** Get a list of all locales. **Parameters:** - `userLocale` (path): string (required) — URL paramater representing locale code to display the data in. **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/projects/{projectId}/translations **List Best Translations (Deprecated)** DEPRECATED: Use GET /api/translations instead.

Get a list of translations. Response will be an object representing a list of translations in the form [Category] => [phrase => translation]. Translations follow the following order: [untranslated DESC, ai_translated DESC, memory_translated DESC, translatable DESC, phrase/label ASC] **Parameters:** - `projectId` (path): string (required) — Id of project - `locale` (query): string (required) - `format` (query): "flat" | "data" — Response format type. When set to 'data', the response will return the schema shown in the endpoint that has this same structure plus /data at the end. When not provided or when using flat, it returns the schema described in this endpoint. **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `locale`.`invalid_option`: The locale is not valid. - `locale`.`invalid_option`: The locale's language is not supported by this project. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/projects/{projectId}/translations/data **List Best Translations in Categorized in Data Format (Deprecated)** DEPRECATED: Use GET /api/translations instead.

Get a list of categorized translations. Categorized results may contain content blocks or translations directly. Translations follow the Translations follow the following order: [untranslated DESC, ai_translated DESC, memory_translated DESC, translatable DESC, phrase/label ASC] **Parameters:** - `projectId` (path): string (required) — Id of project - `locale` (query): string (required) **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `locale`.`invalid_option`: The locale is not valid. - `locale`.`invalid_option`: The locale's language is not supported by this project. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### POST /api/projects/{projectId}/tokens **Create Project Phrases (Deprecated)** DEPRECATED: Use POST /api/translatable-items instead.

Create a set of phrases for a project. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `tokens`: object[] (required) **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### POST /api/projects/{projectId}/content-blocks **Create Content Block (Deprecated)** DEPRECATED: Use POST /api/translatable-items instead.

Create a content block for a project. This endpoint requires a set of phrases that will be related to the newly created content block. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `custom_id`: string (required) — Custom id generated by the client to represent and manipulate the content block. Flat data format output will use this id to index phrases inside content blocks. - `category`: string (required) — The category chosen will be written to every phrase in this content block. - `content`: string (required) — HTML markup to display the content block to translators. - `tokens`: string[] (required) — List of phrases to be added. - `label`: string — Label to identify the content block. If left empty then the first phrase with at least 5 chars will be chosen as the label. **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ## Content Discovery ### POST /api/discovery/hint **Report a page where new content was detected** Called by the SDK when it hits a catalog miss on a session that is not write-enabled. Reports only the page URL — never phrase content — so the page can be queued for automatic discovery. Best-effort: the response is always 204 regardless of whether a render was queued, so the endpoint reveals nothing about key validity or registration state. **Request Body:** Content-Type: application/json - `page_url`: string (required) — URL of the page where the SDK hit a catalog miss on a read-only session. **Responses:** - 204: Hint accepted (or silently ignored) - 422: - 429: ## Billing - Subscriptions ### GET /api/organizations/{organizationId}/enterprise-request **The organization's open Enterprise request** The request the organization is waiting on (pending, seen or contacted), or null when there is none. A closed request is not returned. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success; `data` is null when no request is open - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/enterprise-request **Ask to be contacted about Enterprise** Sends the team a request to talk about Enterprise. The organization's plan does not change. While a request is open, sending another returns that one with 200 and creates nothing. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `message`: string — What the customer wants the team to know, optional **Responses:** - 200: A request was already open; it is returned unchanged - 201: Created - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/billing/subscriptions **Get all subscriptions** Retrieve all subscriptions with their related plan cycle and organization. Can optionally filter by organization ID. **Parameters:** - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `expires_at`, `status` **Default order:** `expires_at:asc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` - `organization_id` (query): string — Optional: Filter subscriptions by organization ID **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/billing/subscriptions/{subscriptionId} **Get subscription details** Get subscription details by ID **Parameters:** - `subscriptionId` (path): string (required) — Id of subscription **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/billing/subscriptions/recover **Recover a frozen or suspended subscription** Recover a frozen or suspended subscription by either using an existing payment method or adding a new one. On success the subscription status is set back to active and full plan limits are restored. **Request Body:** Content-Type: application/json - `organization_id`: string (required) — The organization ID whose subscription to recover - `cc_id`: string — The credit card ID for the existing credit card to use for this transaction - `credit_card`: object — New credit card information to use for this transaction - `save_credit_card`: boolean — Keep the card typed in credit_card for later payments. Off unless sent; a plan with auto-renew on keeps its card regardless, since renewals charge it. A card already saved stays saved either way. **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 401: - 402: Possible errors: - `no_valid_payment_method`: No default payment method found for the entity. - `payment_method_rejected`: This card was declined. Please use a different card or contact your bank. - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `recoverable_subscription_not_found`: No frozen or suspended subscription found for recover. - 422: Possible errors: - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `validation_failed`: The request failed validation. Possible validation errors: - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 502: - 503: ### POST /api/billing/subscriptions/update **Update user subscription plan (upgrade, downgrade)** Update user to a new plan cycle (upgrade, downgrade) and process the change as needed. **Request Body:** Content-Type: application/json - `organization_id`: string (required) — The organization ID whose subscription to update - `plan_type`: "free" | "starter" | "pro" | "business" | "enterprise" - `plan_cycle`: "monthly" | "quarterly" | "biannual" | "yearly" | "lifetime" - `credit_card`: object - `cc_id`: string — The credit card id for the existing credit card - `billing_address`: object — Optional. Without it, tax follows the organization's country and VAT ID. - `user_id`: string - `auto_renew`: boolean — Renew automatically by charging the card at the end of each period. On by default for a new plan; a plan change keeps the current setting unless this is sent. - `save_credit_card`: boolean — Keep the card typed in credit_card for later payments. Off unless sent; a plan with auto-renew on keeps its card regardless, since renewals charge it. A card already saved stays saved either way. **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 401: - 402: Possible errors: - `no_valid_payment_method`: No default payment method found for the entity. - `payment_method_rejected`: This card was declined. Please use a different card or contact your bank. - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `invalid_plan_cycle`: The requested plan type and billing cycle combination does not exist. - `no_active_subscription`: Organization does not have an active subscription - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `same_plan_cycle`: Cannot update subscription to the same plan cycle. - `validation_failed`: The request failed validation. Possible validation errors: - `credit_card`.`required`: A credit card is required for paid plans. - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `billing_address.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `billing_address.vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 502: - 503: ### POST /api/billing/subscriptions/auto-renew **Set subscription auto-renewal** Enable or disable automatic renewal of the organization's subscription. When disabled, the subscription stays active until the end of the current billing cycle and is then retired instead of being charged or invoiced again. Re-enabling before the cycle ends resumes normal renewal and clears any pending end-of-cycle expiration. **Request Body:** Content-Type: application/json - `organization_id`: string (required) — The organization ID whose subscription to update - `auto_renew`: boolean (required) — Whether the subscription renews automatically at the end of each billing cycle **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `auto_renew_unavailable`: Organization does not have a subscription that can change auto-renewal - `validation_failed`: The request failed validation. - 429: ### POST /api/billing/subscriptions/billing-address **Update subscription billing address** Update the billing address for a subscription by linking to a user billing address. **Request Body:** Content-Type: application/json - `organization_id`: string (required) — The ID of the organization to set the billing address for - `project_id`: string — The ID of the project to set the billing address for - `name`: string — The name on the billing address - `address_1`: string — The first line of the address - `address_2`: string — The second line of the address (optional) - `city`: string — The city of the address - `state`: string — The state/province/region of the address - `zip`: string — The postal/zip code - `country_code`: string — The two-letter country code - `vat_id`: string — VAT/Tax registration ID (e.g. EU VAT number) - `user_billing_address_id`: string — The ID of the user billing address to set as the default billing address **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `country_code`.`required`: The country is required when saved billing address is not present. - `vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - 429: ## Health ### GET /api/up **Liveness Probe** Returns 200 immediately if the application process is responding. Performs no dependency checks. Intended for load balancers and orchestrators. **Responses:** - 200: Application is responding - 429: ### GET /api/health **Readiness Probe** Returns the status of critical dependencies (database, redis, queue). Returns 200 when all critical checks pass (status: ok or degraded), 503 when a critical dependency is unreachable (status: down). **Responses:** - 200: All critical dependencies reachable - 429: - 503: One or more critical dependencies unreachable ## Invitations ### GET /api/invitations **Get User Invitation Details by Token** Get a single invitation by its token, provided as a query parameter. **Parameters:** - `token` (query): string — Invitation token. Must be provided if invitationId is not provided. **Responses:** - 200: Success - 404: - 422: - 429: ### GET /api/invitations/{invitationId} **Get User Invitation Details** Get a single invitation by its id. Only entity admins or invitees can access it. **Parameters:** - `invitationId` (path): string (required) — Id of invitation **Responses:** - 200: Success - 404: - 422: - 429: ### DELETE /api/invitations/{invitationId} **Revoke Invitation** Revoke a pending invitation. Only the invitation sender or entity owners can revoke invitations. **Parameters:** - `invitationId` (path): string (required) — Id of invitation **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/invitations/{invitationId}/resend **Resend User Invitation** Resend an invitation email to the user. **Parameters:** - `invitationId` (path): string (required) — Id of invitation **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/invitations/accept/{invitationToken} **Accept Invitation** Endpoint for users to accept an invitation via email link. For paid plan invitations the payment fields select how the subscription is billed: a credit card (new card or stored cc_id) activates immediately, wire transfer (Enterprise only) creates the subscription frozen until the invoice is paid. **Parameters:** - `invitationToken` (path): string (required) — Activation token received by user through email. **Request Body:** Content-Type: application/json - `credit_card`: object — New card to pay with when accepting a paid plan invitation - `cc_id`: string — Id of an already stored payment method to pay with - `subscription_billing_address`: object - `payment_method_type`: "credit_card" | "wire_transfer" — Only relevant for plan invitations. Wire transfer is available for Enterprise plans only - `auto_renew`: boolean — Whether the subscription renews automatically at the end of each billing cycle - `save_credit_card`: boolean — Keep the card typed in credit_card for later payments when auto_renew is off. Off unless sent; with auto-renew on the card is kept, since renewals charge it. **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 402: - 404: - 422: Possible errors: - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `translator_limit_reached`: Your plan's translator limit is {limit}. - `user_limit_reached`: Your plan's user limit is {limit}. - `validation_failed`: The request failed validation. Possible validation errors: - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `expired`: This invitation has expired. - `not_available`: This plan is no longer available for new subscriptions. - `not_available`: The invited plan has no available billing cycle. - `not_allowed`: Wire transfer is only available for Enterprise plans. - `not_allowed`: This plan has no fixed price. Please use wire transfer and contact support to arrange payment. - `required`: A payment method is required to subscribe to this plan. - `subscription_billing_address.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `subscription_billing_address.vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - `token`.`not_found`: The invitation token is invalid. - `token`.`expired`: This invitation has expired. - `auth`.`required`: Sign in to accept this invitation. - 429: - 502: - 503: ### POST /api/invitations/decline/{invitationToken} **Decline Invitation** Endpoint for users to decline an invitation via email link. **Parameters:** - `invitationToken` (path): string (required) — Activation token received by user through email. **Responses:** - 200: Success - 422: `validation_failed`: The request failed validation. Possible validation errors: - `token`.`not_found`: The invitation token is invalid. - 429: ### POST /api/me/invitations/{invitationId}/accept **Accept One of My Invitations** Accepts an invitation listed by GET me/invitations, by its id: the same as POST invitations/accept/{invitationToken}, including the payment fields for a paid plan invitation, for the signed-in invitee only, once their address is verified. Any other invitation answers 404. **Parameters:** - `invitationId` (path): string (required) — Id of invitation **Request Body:** Content-Type: application/json - `credit_card`: object — New card to pay with when accepting a paid plan invitation - `cc_id`: string — Id of an already stored payment method to pay with - `subscription_billing_address`: object - `payment_method_type`: "credit_card" | "wire_transfer" — Only relevant for plan invitations. Wire transfer is available for Enterprise plans only - `auto_renew`: boolean — Whether the subscription renews automatically at the end of each billing cycle - `save_credit_card`: boolean — Keep the card typed in credit_card for later payments when auto_renew is off. Off unless sent; with auto-renew on the card is kept, since renewals charge it. **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review, as for POST invitations/accept/{invitationToken}. - 401: - 402: - 403: - 404: Possible errors: - `invitation_not_found`: Invalid invitation. - `not_found`: Resource not found - 422: Possible errors: - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `translator_limit_reached`: Your plan's translator limit is {limit}. - `user_limit_reached`: Your plan's user limit is {limit}. - `validation_failed`: The request failed validation. Possible validation errors: - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `expired`: This invitation has expired. - `not_available`: This plan is no longer available for new subscriptions. - `not_available`: The invited plan has no available billing cycle. - `not_allowed`: Wire transfer is only available for Enterprise plans. - `not_allowed`: This plan has no fixed price. Please use wire transfer and contact support to arrange payment. - `required`: A payment method is required to subscribe to this plan. - `subscription_billing_address.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `subscription_billing_address.vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - `token`.`expired`: This invitation has expired. - 429: - 502: - 503: ### POST /api/me/invitations/{invitationId}/decline **Decline One of My Invitations** Declines an invitation listed by GET me/invitations, by its id, for the signed-in invitee only, once their address is verified. Any other invitation answers 404. **Parameters:** - `invitationId` (path): string (required) — Id of invitation **Responses:** - 200: Success - 401: - 403: - 404: Possible errors: - `invitation_not_found`: Invalid invitation. - `not_found`: Resource not found - 429: ## Billing - Invoices ### GET /api/billing/invoices **Get all invoices for the authenticated user** Fetch all invoices for the currently authenticated user. **Parameters:** - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `file_name`, `status`, `type` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `status`, `type` **Responses:** - 200: Success - 401: - 403: - 404: - 422: - 429: ### GET /api/billing/invoices/{invoiceId} **Get a specific invoice for the authenticated user** Fetch a specific invoice for the currently authenticated user. **Parameters:** - `invoiceId` (path): string (required) — The ID of the invoice to fetch **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/billing/tax-quote **Quote the tax for a charge before it happens** Prices are net: when the organization's billing address makes it taxable and the tax feature is enabled, VAT is added on top of the net amount at charge time. This endpoint returns the exact treatment, rate, tax and gross total that a charge of the given net amount would produce, so clients can display it before the customer commits (plan checkout, credit purchases, auto-recharge settings). **Parameters:** - `organization_id` (query): string (required) - `amount` (query): number (required) **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## Translations - Machine ### POST /api/translations/machine/translate **Machine Translate Translatable Items** Machine translate a list of translatable items. For content blocks, all associated phrases will be translated. Optionally specify target locales; if not provided, all default target locales for the project will be used. **Request Body:** Content-Type: application/json - `translatable_item_ids`: string[] (required) — List of translatable item IDs to machine translate. For content blocks, all associated phrases will be translated. All translatable items must belong to the same project. - `locales`: string[] — List of target locales to translate to. If not provided, all project target locales will be used. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `machine_translation_disabled`: Machine translation is temporarily disabled due to unusual usage patterns. Please contact your administrator to resume service. - 422: `validation_failed`: The request failed validation. Possible validation errors: - `translatable_item_ids`.`required`: At least one translatable item is required. - `translatable_item_ids`.`not_found`: None of the translatable items were found. - `translatable_item_ids`.`invalid_option`: All translatable items must belong to the same project. - `locales`.`invalid_type`: The locales must be a list of locale codes. - `translatable_item_ids`.`not_found`: The translatable item is invalid. - `locales`.`invalid_option`: These locales are not target locales of this project: {locales}. - 429: ### GET /api/translations/machine/{translatableItemId} **Get Machine Translation for Translatable Item** Get a machine translation for a translatable item and locale. **Parameters:** - `translatableItemId` (path): string (required) — Translatable item ID to delete - `locale` (query): string (required) **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `locale`.`invalid_option`: The locale is not valid. - `locale`.`invalid_option`: The locale is not a target locale of this project. - 429: ### POST /api/translations/machine/translate-untranslated **Machine Translate Untranslated Phrases** Machine translate all untranslated phrases in a project, into one or more target locales, using the machine translation service defined in project settings. Pass `locales` with the target locales to translate into, or `use_target_locales: true` to translate into every target locale the project has. Each locale is dispatched, tracked and charged separately, so the response carries a per-locale `locales` array alongside the summary message. Locales are funded in the order given. A locale the balance cannot pay for, after the ones queued before it, is not dispatched and is reported as `insufficient_balance` with its `cost`. A locale that would take the project past its monthly usage limit is reported as `monthly_limit_exceeded`. When no requested locale can be queued for either reason, the request fails with 402 and nothing is dispatched: `monthly_usage_limit_exceeded` when the limit refused a locale, otherwise `insufficient_balance`. **Request Body:** Content-Type: application/json - `project_id`: string (required) - `locales`: string[] — Target locales to machine translate the project's untranslated phrases into. Required and non-empty unless use_target_locales is true, in which case it is ignored. Every locale must be one of the project's target locales. - `use_target_locales`: boolean — Translate into every target locale configured for the project. Defaults to false: a run normally has to name the locales it is paying for, since each one is billed separately. When true, locales is ignored. **Responses:** - 200: Nothing to translate — all phrases already translated in every requested locale - 202: Accepted — untranslated phrases queued for at least one locale - 401: - 402: Possible errors: - `insufficient_balance`: Insufficient balance to process request - `monthly_usage_limit_exceeded`: Monthly credit usage limit exceeded - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `machine_translation_disabled`: Machine translation is temporarily disabled due to unusual usage patterns. Please contact your administrator to resume service. - 404: - 409: - 422: Possible errors: - `no_target_locales`: This project has no target locales configured - `validation_failed`: The request failed validation. Possible validation errors: - `locales`.`required`: At least one locale is required unless use_target_locales is true. - `locales`.`invalid_type`: The locales must be a list of locale codes. - `locales`.`invalid_option`: These locales are not target locales of this project: {locales}. - 429: ### GET /api/projects/{projectId}/machine-translation-quote **Quote Machine Translating Untranslated Phrases** Price a machine translation of everything untranslated in the project, on demand — read only, nothing is dispatched and nothing is charged. The figures are exact, not estimates: the same untranslated set and per-word rate a run started now would use, so they can only drift lower as phrases get translated in the meantime. Works regardless of the machine_translate_added_target_locales setting, so it can quote a project whose locale addition was skipped. The response also says whether the balances already in place cover the charge (available_balance / covered_cost / shortfall). **Parameters:** - `projectId` (path): string (required) — Id of project - `locales[]` (query): string[] — Target locales to quote. Repeat the parameter for several: locales[]=de-de&locales[]=fr-fr. Required and non-empty unless use_target_locales=true. Every locale must be one of the project's target locales. - `use_target_locales` (query): boolean — Quote every target locale configured for the project. When true, locales is ignored. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `no_target_locales`: This project has no target locales configured - `validation_failed`: The request failed validation. Possible validation errors: - `locales`.`required`: At least one locale is required unless use_target_locales is true. - `locales`.`invalid_type`: The locales must be a list of locale codes. - `locales`.`invalid_option`: These locales are not target locales of this project: {locales}. - 429: ### GET /api/organizations/{organizationId}/machine-translation-rates **Get the organization's machine translation rate card** Per-word machine translation prices as they apply to this organization: the band-A price per word with the organization's plan rate multiplier already applied, plus the locale price bands and their multipliers. The effective price for a run is price per word × band multiplier × source words × target locales; this is exactly what the run will be charged — quotes are deterministic. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## Internal ### POST /api/internal/mt-results **langsys-ai MT batch results webhook (internal)** Internal, HMAC-signed webhook. langsys-ai POSTs each finished machine-translation batch here (results inline, or a pointer when oversized). Idempotent: duplicate deliveries are acknowledged without effect. Not for external consumption. **Responses:** - 200: Acknowledged - 401: Possible errors: - `webhook_signature_expired`: Signature timestamp outside the replay window - `webhook_signature_invalid`: Invalid signature - `webhook_signature_missing`: Missing or malformed signature header - 429: ### POST /api/internal/render-results **Discovery renderer outcome webhook (internal)** Internal, HMAC-signed webhook. The discovery renderer POSTs the outcome of each page it was asked to load — whether the page was reachable and whether its content changed. Carries no registrations: those are performed by the SDK inside the rendered page. Idempotent: an unknown or already-settled render id is acknowledged without effect. Not for external consumption. **Request Body:** Content-Type: application/json - `render_id`: string (required) — Correlation id issued when the render was submitted. - `outcome`: "rendered" | "unchanged" | "login_wall" | "unreachable" | "failed" (required) — What happened when the renderer loaded the page. - `content_hash`: string — Hash of the rendered content, used to skip re-rendering an unchanged page. - `final_url`: string — Where the renderer actually ended up, when that differs from the requested page — the login or SSO URL for a login_wall outcome. Makes the pending-content worklist actionable without a human having to reproduce the redirect. **Responses:** - 204: Acknowledged - 401: Possible errors: - `webhook_signature_expired`: Signature timestamp outside the replay window - `webhook_signature_invalid`: Invalid signature - `webhook_signature_missing`: Missing or malformed signature header - 422: - 429: ## User ### POST /api/me/native-ticket **Mint a Native Passkey Ticket** A single-use ticket, valid 120 seconds, that lets the hosted page add a passkey to this account. Like POST me/passkeys/options, it needs a session signed in within the last 24 hours or a step_up_token; otherwise 403 step_up_required, before the in-app browser is opened. An unverified email gets 403 email_not_verified. **Request Body:** Content-Type: application/json - `step_up_token`: string — A step_up_token from POST me/step-up. Needed when the session is more than 24 hours old. **Responses:** - 201: Created - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `email_not_verified`: Confirm your email address first: sign in with the code we email you. - `step_up_required`: Confirm it is you first, with a passkey or an emailed code. - 422: - 429: ### GET /api/me/passkeys **List Passkeys** The passkeys that can sign in to this account, most recently used first. **Responses:** - 200: Success - 401: - 403: - 429: ### POST /api/me/passkeys **Add a Passkey** Verifies the registration against the challenge from POST me/passkeys/options and saves the passkey. The challenge only works for the user it was issued to. **Request Body:** Content-Type: application/json - `challenge_id`: string (required) — The challenge_id returned with the creation options. - `credential`: string[] (required) — The RegistrationResponseJSON from navigator.credentials.create() (as @simplewebauthn/browser returns it). - `name`: string — A name the user will recognise the passkey by. Defaults to "Passkey". **Responses:** - 201: Created - 401: - 403: - 422: Possible errors: - `passkey_verification_failed`: That passkey couldn't be verified. Try again, or sign in with an emailed code. - `validation_failed`: The request failed validation. - 429: ### POST /api/me/passkeys/options **Get Passkey Creation Options** Returns creation options for navigator.credentials.create() and a challenge_id. Allowed when the session was signed in within the last 24 hours; otherwise send a step_up_token from POST me/step-up, or get 403 step_up_required. An account whose email is not verified yet gets 403 email_not_verified: signing in with an emailed code verifies it. The account's existing passkeys are excluded, so re-registering the same authenticator is refused by the browser. **Request Body:** Content-Type: application/json - `step_up_token`: string — A step_up_token from POST me/step-up. Needed when the session is more than 24 hours old. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `email_not_verified`: Confirm your email address first: sign in with the code we email you. - `step_up_required`: Confirm it is you first, with a passkey or an emailed code. - 422: - 429: ### DELETE /api/me/passkeys/{passkeyId} **Delete a Passkey** The passkey stops working immediately. Deleting the last one is allowed: the account can still sign in with an emailed code. **Parameters:** - `passkeyId` (path): string (required) **Responses:** - 204: Deleted - 401: - 403: - 404: - 429: ### PATCH /api/me/passkeys/{passkeyId} **Rename a Passkey** **Parameters:** - `passkeyId` (path): string (required) **Request Body:** Content-Type: application/json - `name`: string (required) — New name for the passkey. **Responses:** - 200: Success - 401: - 403: - 404: - 422: - 429: ### GET /api/me/sessions **List Active Sessions** Get all active sessions for the authenticated user. **Responses:** - 200: Success - 401: - 403: - 429: ### POST /api/me/sessions **Mint MCP Session Token** Mint the session an external client (an MCP server) uses on the signed-in account's behalf, bound to a new device_id. The session is MCP-scoped (`scope` = `mcp`): the API admits it only on the operations listed in api-docs-mcp.json and answers 403 mcp_scope_exceeded on every other one, this endpoint included, so nothing minted here can be widened. It expires after three months, appears in GET /api/me/sessions and is revocable via DELETE /api/me/sessions/{sessionId}. Every request made with the returned token must send x-device-id set to the same device_id. **Request Body:** Content-Type: application/json - `device_id`: string (required) — The new client-generated device identifier the minted token will be bound to. The same value must be sent as the x-device-id header on every subsequent request made with the returned token. **Responses:** - 201: Created - 401: - 403: - 422: Possible errors: - `session_device_unchanged`: The new device_id must differ from your current session device_id. - `validation_failed`: The request failed validation. - 429: ### DELETE /api/me/sessions/{sessionId} **Terminate Session** Terminate a specific session by token ID. You cannot terminate your current session — use the logout endpoint instead. **Parameters:** - `sessionId` (path): string (required) — UUID of the session to terminate **Responses:** - 200: Success - 401: - 403: Possible errors: - `current_session_protected`: Cannot terminate your current session. Use the logout endpoint instead. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - 404: - 429: ### POST /api/me/step-up/email **Email a Confirmation Code** Emails the account's current address a 6-digit confirmation code, valid once for 10 minutes. Exchange it at POST me/step-up. **Responses:** - 202: Accepted - 401: - 403: - 429: ### POST /api/me/step-up/passkey/options **Get Passkey Confirmation Options** Request options for navigator.credentials.get(), limited to this account's passkeys. Send the result to POST me/step-up. **Responses:** - 200: Success - 401: - 403: - 429: ### POST /api/me/step-up **Confirm It Is You** Exchanges a confirmation code, or a passkey assertion, for a step_up_token: single use, valid 5 minutes, and only for the session that asked. Bearer-token sessions only: a cookie session gets 403 step_up_required. Send it with DELETE me, PATCH me/email, or to add a passkey from a session older than 24 hours. **Request Body:** Content-Type: application/json - `code`: string — The code from the confirmation email. Send this, or a passkey assertion. - `challenge_id`: string — The challenge_id from POST me/step-up/passkey/options. - `credential`: string[] — The AuthenticationResponseJSON from navigator.credentials.get(). **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `step_up_required`: Confirm it is you first, with a passkey or an emailed code. - 422: Possible errors: - `email_code_invalid`: That code didn't work. Check it, or send a new one. - `passkey_verification_failed`: That passkey couldn't be verified. Try again, or sign in with an emailed code. - `validation_failed`: The request failed validation. Possible validation errors: - `code`.`required`: The code is required when challenge ID is not present. - `challenge_id`.`required`: The challenge ID is required when code is not present. - `credential`.`required`: The credential is required when challenge ID is present. - 429: ### GET /api/me **Get User Profile** Get your profile information. An account that signed up while invited to a plan starts with no organization; once it has none of any kind and no plan invitation still waiting (it declined, or the invitation expired), this call gives it the free organization it would have had. **Responses:** - 200: Success - 401: - 403: - 429: ### DELETE /api/me **Delete Account** Permanently delete your account. Requires a step_up_token from POST me/step-up; without one, 403 step_up_required. You must transfer ownership of (via /organizations/{org}/transfer-ownership) or delete every organization you own before this endpoint will succeed. Data created by you in organizations owned by others is auto-reassigned to the organization owner. **Request Body:** Content-Type: application/json - `step_up_token`: string — A step_up_token from POST me/step-up, proving it is you. - `password`: string — Retired. Sending a password answers 410 passwords_retired. - `mode`: string - `target_email`: string **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `step_up_required`: Confirm it is you first, with a passkey or an emailed code. - `super_admin_self_deletion`: Super admin accounts cannot be self-deleted. - 410: - 422: Possible errors: - `account_owns_organizations`: You must transfer ownership of or delete every organization you own before deleting your account. - `validation_failed`: The request failed validation. - 429: ### PATCH /api/me **Update User Profile** Update your profile. An account created by email sign-up starts with an empty firstname and lastname; onboarding sets them here, and the first time it does, the organization the account was given (named after its address) takes the name too, unless it was renamed already. **Request Body:** Content-Type: application/json - `firstname`: string - `lastname`: string - `phone`: string - `locale`: string - `avatar`: object — Avatar object with meta data and urls for the different sizes. Defaults to gravatar urls if not found. - `source_locales`: string[] — List of locales user can translate from - `target_locales`: string[] — List of locales user can translate to **Responses:** - 200: Success - 401: - 403: - 422: - 429: ### GET /api/me/settings **Get User Settings** Get your profile settings **Responses:** - 200: Success - 401: - 403: - 429: ### PATCH /api/me/settings **Update User Settings** Update your profile settings **Request Body:** Content-Type: application/json - `notifications`: object — The user notification settings - `billing`: object — The user billing settings **Responses:** - 200: Success - 401: - 403: - 422: - 429: ### PATCH /api/me/email **Update User Email** Update your email. Requires a step_up_token from POST me/step-up; without one, 403 step_up_required. Sends a verification email to the new address and switches once it is verified, and tells the current address about the change. **Request Body:** Content-Type: application/json - `email`: string (required) - `step_up_token`: string — A step_up_token from POST me/step-up, proving it is you. - `password`: string — Retired. Sending a password answers 410 passwords_retired. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `step_up_required`: Confirm it is you first, with a passkey or an emailed code. - 410: - 422: Possible errors: - `email_undeliverable`: The email address could not be verified. - `validation_failed`: The request failed validation. - 429: ### PATCH /api/me/password (DEPRECATED) **Update User Password (retired)** Passwords have been retired: this always answers 410 passwords_retired. Manage passkeys at /api/me/passkeys instead. **Responses:** - 401: - 403: - 410: Passwords are retired - 429: ### GET /api/me/invitations **List User Invitations** Get all the active - non expired invitations the authenticated user has available, including ones sent to their verified address before the account existed. Accept or decline one with POST me/invitations/{invitationId}/accept or decline. Pagination parameters are optional. **Parameters:** - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `email`, `role`, `status` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: - 429: ### GET /api/me/deletion-report **Get Account Deletion Report** Preview what will be affected by deleting your account. Returns owned organizations with their projects, and counts of other owned data, memberships, and personal data. **Responses:** - 200: Success - 401: - 403: - 429: ## Notification ### GET /api/notifications **Get User's Notifications** Get all notifications the authenticated user has. Pagination parameters are optional. Results can be ordered by: message, action, read_at, created_at, updated_at, id. **Parameters:** - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `read_at`, `action` **Responses:** - 200: success - 401: - 403: - 429: ### GET /api/notifications/{notificationId} **Get Single Notification** Get a single notification by its id. The notification must be owned by the authenticated user **Parameters:** - `notificationId` (path): string (required) — Id of notification **Responses:** - 200: Success - 401: - 403: - 404: - 429: ### DELETE /api/notifications/{notificationId} **Delete Notification** Delete a notification. **Parameters:** - `notificationId` (path): string (required) — Id of notification **Responses:** - 200: Success - 401: - 403: - 404: - 429: ### PATCH /api/notifications/read **Mark Notifications as Read** Marks one or multiple notifications as read via ids in the request body. To mark a single notification, send an array with one id. All notifications must be owned by the authenticated user. If a notification is not owned by the authenticated user or an invalid id is provided, it will be ignored. **Request Body:** Content-Type: application/json - `notification_ids`: string[] — Array of notification IDs to mark as read or unread **Responses:** - 200: Success - 401: - 403: - 404: - 422: - 429: ### PATCH /api/notifications/unread **Mark Notifications as Unread** Marks one or multiple notifications as unread via ids in the request body. To mark a single notification, send an array with one id. All notifications must be owned by the authenticated user. If a notification is not owned by the authenticated user or an invalid id is provided, it will be ignored. **Request Body:** Content-Type: application/json - `notification_ids`: string[] — Array of notification IDs to mark as read or unread **Responses:** - 200: Success - 401: - 403: - 404: - 422: - 429: ### PATCH /api/notifications/all/read **Mark All Notifications as Read** Marks all the logged user notification as read. **Responses:** - 200: Success - 401: - 403: - 429: ### PATCH /api/notifications/all/unread **Mark All Notifications as Unread** Marks all the logged user notification as unread. **Responses:** - 200: success - 401: - 403: - 429: ## Organization - Credits ### GET /api/organizations/{organizationId}/credits/transactions **Get Organization Credit Transactions** Get the credit balance of an organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `amount`, `created_at`, `type` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `type` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/organizations/{organizationId}/credits **Get Organization Credits** Get the credits of an organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/credits **Add Credit to Organization** Add credit to an organization's balance. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `amount`: number (required) — Amount of credit to add. - `cc_id`: string — The credit card id for the existing credit card - `credit_card`: object — The credit card data for the new credit card - `save_credit_card`: boolean — Keep the card typed in credit_card for later payments. Off unless sent. A card already saved stays saved either way. - `use_subscription_billing_address`: boolean — Whether to use the subscription billing address in the invoice, if false, the payment method address will be used if user_organization_address is false - `use_organization_address`: boolean — Whether to use the organization address in the invoice. This will only apply when adding credit to a specific organization or project - `user_billing_address_id`: string — ID of user billing address to use for this transaction. Optional: without one, tax follows the organization's country and VAT ID. - `payment_provider`: string **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 401: - 402: Possible errors: - `no_valid_payment_method`: No default payment method found for the entity. - `payment_method_rejected`: This card was declined. Please use a different card or contact your bank. - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `validation_failed`: The request failed validation. Possible validation errors: - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `user_billing_address_id`.`not_found`: The selected billing address is invalid. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 502: - 503: ### POST /api/organizations/{organizationId}/credits/transfer/project/{projectId} **Transfer Credits from Organization to Project** **Parameters:** - `organizationId` (path): integer (required) - `projectId` (path): string (required) **Request Body:** Content-Type: application/json - `prepaid_credits`: number — Amount of prepaid credits to transfer. - `free_credits`: number — Amount of free credits to transfer. **Responses:** - 200: Success - 401: - 402: Possible errors: - `insufficient_balance`: Insufficient balance to process request - `insufficient_free_credits`: Insufficient free credits to process request - `insufficient_prepaid_credits`: Insufficient prepaid credits to process request - `monthly_usage_limit_exceeded`: Monthly credit usage limit exceeded - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `project_not_in_organization`: Project does not belong to the specified organization. - 422: `validation_failed`: The request failed validation. Possible validation errors: - `prepaid_credits`.`required`: Either the prepaid credits or the free credits must be greater than 0. - 429: ### GET /api/organizations/{organizationId}/credits/summary **Get Organization Credit Usage Summary** Get the aggregated credit usage summary of an organization, optionally filtered by date range. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `start_date` (query): string - `end_date` (query): string **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## Organization ### GET /api/organizations **List All Organizations** Get a paginated list of all organizations. Pagination parameters are optional. **Parameters:** - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `admin`, `email`, `last_activity_at`, `name`, `user_joined_at` **Default order:** `name:asc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `name`, `email`, `admin` **Responses:** - 200: Success - 401: - 403: - 429: ### POST /api/organizations **Create New Organization** Create an organization. Only available for organization owners. **Request Body:** Content-Type: application/json - `name`: string (required) - `email`: string (required) - `website_url`: string - `icon`: object - `logo`: object - `settings`: object - `auto_recharge_credit_card_id`: string - `address`: object - `subscription_billing_address`: object - `plan_type`: "free" | "starter" | "pro" | "business" | "enterprise" - `credit_card`: object - `cc_id`: string — The credit card id for the existing credit card - `organization`: object **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 401: - 402: Possible errors: - `no_valid_payment_method`: No default payment method found for the entity. - `payment_method_rejected`: This card was declined. Please use a different card or contact your bank. - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 422: Possible errors: - `invalid_plan_cycle`: The requested plan type and billing cycle combination does not exist. - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `validation_failed`: The request failed validation. Possible validation errors: - `address.address_1`.`required`: The address line 1 is required when address is present. - `address.city`.`required`: The city is required when address is present. - `address.state`.`required`: The state is required when address is present. - `address.zip`.`required`: The ZIP code is required when address is present. - `address.country_code`.`required`: The country is required when address is present. - `subscription_billing_address`.`required`: A billing address is required for paid plans. - `subscription_billing_address.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `subscription_billing_address.vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - `credit_card`.`required`: A credit card is required for paid plans. - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 502: - 503: ### GET /api/organizations/{organizationId} **Get Organization Details** Get a single organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/organizations/{organizationId} **Delete Organization** Delete an organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 409: - 429: ### PATCH /api/organizations/{organizationId} **Update Organization** Update an organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `name`: string - `email`: string - `website_url`: string - `icon`: object - `logo`: object - `address`: object - `settings`: object - `country_code`: string — The organization's country, which decides tax on its charges - `vat_id`: string — The organization's VAT ID; an EU business with one is not charged VAT (reverse charge) **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `address.address_1`.`required`: The address line 1 is required when address is present. - `address.city`.`required`: The city is required when address is present. - `address.state`.`required`: The state is required when address is present. - `address.zip`.`required`: The ZIP code is required when address is present. - `address.country_code`.`required`: The country is required when address is present. - 429: ### POST /api/organizations/{organizationId}/setup/dismiss **Dismiss the Getting started checklist** Hides the organization's Getting started checklist on every device and for the MCP agent. The checklist's steps are not changed. Calling it again changes nothing. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/plan-choice **Keep the organization's current plan** Records that the owner settled the plan during onboarding without changing it, e.g. kept Free, so `plan_chosen_at` is set and the plan step is not offered again. Changing plan, accepting a plan invitation and asking about Enterprise set it on their own. The first time the plan is settled, however it is, the owner gets the welcome email naming it. Calling it again changes nothing. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/transfer-ownership **Transfer Organization Ownership** Transfer ownership of the organization to a new user. Only available for organization owner. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `user_id`: string — The id of the user to invite. If this field is provided, then the email field will be ignored. - `email`: string — This field should be provided if the userId is not provided. It should be used for users who are not Langsys users. If the email provided is for an existing user then this will behave in the same way as sending the userId. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `email_undeliverable`: The email address could not be verified. - `validation_failed`: The request failed validation. Possible validation errors: - `user_id`.`required`: The user is required when email is not present. - `email`.`already_member`: The user is already part of this organization. - `email`.`already_member`: The user is already part of this project. - `email`.`already_owner`: The user is already the owner. - 429: ## Organization - Domains ### GET /api/organizations/{organizationId}/organization-domains **List Organization Domains** Get all organization domains for an organization **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/organization-domains **Create Organization Domain** Create a new organization domain for the organization. The returned objects contains a verification token that must be added as a DNS TXT record using the format `langsys-verification=verification_token`. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `domain`: string (required) — The domain to verify **Responses:** - 201: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `domain`.`invalid_format`: The domain format is invalid. - `domain`.`already_taken`: This domain has already been added for verification. - 429: ### GET /api/organizations/{organizationId}/organization-domains/{organizationDomainId} **Get Organization Domain** Get details of a specific organization domain **Parameters:** - `organizationId` (path): string (required) — Id of organization - `organizationDomainId` (path): string (required) **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `organization_domain_not_found`: Organization domain not found. - 429: ### DELETE /api/organizations/{organizationId}/organization-domains/{organizationDomainId} **Delete Organization Domain** Delete a organization domain record **Parameters:** - `organizationId` (path): string (required) — Id of organization - `organizationDomainId` (path): string (required) **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `organization_domain_not_found`: Organization domain not found. - 429: ### POST /api/organizations/{organizationId}/organization-domains/{organizationDomainId}/verify/ **Start Organization Domain Verification** Start the DNS verification process. The system will check for the DNS TXT record immediately. If not found, it will continue checking every 5 minutes for up to 48 hours. It will fall into invalid status if the verification fails after 48 hours. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `organizationDomainId` (path): string (required) **Responses:** - 200: Organization domain verification started - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `organization_domain_not_found`: Organization domain not found. - 422: - 429: ### POST /api/organizations/{organizationId}/organization-domains/{organizationDomainId}/recheck **Recheck Organization Domain Verification** Restart verification for an invalid organization domain. Only works for domains with 'invalid' status. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `organizationDomainId` (path): string (required) **Responses:** - 200: Organization domain verification restarted - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `organization_domain_not_found`: Organization domain not found. - 422: `validation_failed`: The request failed validation. Possible validation errors: - `status`.`not_allowed`: Only invalid verifications can be rechecked. - 429: ## Organization - Invitations ### GET /api/organizations/{organizationId}/invitations **List Organization's Sent Invitations** Get all sent invites for an organization. Only available for organization admins. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `email`, `role`, `status` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/invitations **Invite User to Organization** Invite a user to an organization. Only available for organization admins.

**This function will send an invitation email to user. An invitation takes a seat as soon as it is sent: one that would take the organization past its plan user limit, counting live invitations, answers 422 user_limit_reached. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `user_id`: string — The id of the user to invite. If provided, the email field will be ignored. - `role`: "organization_admin" | "organization_user" | "billing_manager" — Role of the user in the organization - `email`: string — Provide an email if user is new. Email can also be provided for existing users if userId is not provided in the URI. - `disabled_projects`: string[] — A list of the ids of projects that will be disabled for the user. This list will sync with the currently disabled projects of the user. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `email_undeliverable`: The email address could not be verified. - `translator_limit_reached`: Your plan's translator limit is {limit}. - `user_limit_reached`: Your plan's user limit is {limit}. - `validation_failed`: The request failed validation. Possible validation errors: - `user_id`.`required`: The user is required when email is not present. - `email`.`required`: The email is required when user is not present. - `email`.`already_member`: The user is already part of this organization. - `email`.`already_member`: The user is already part of this project. - `email`.`already_owner`: The user is already the owner. - `disabled_projects`.`invalid_option`: The list of disabled projects is invalid. - 429: ### GET /api/organizations/invitations/all **List All Invitations for Admin's Organizations** Retrieve all organizations invitations that have been sent where the logged user holds an admin role. **Parameters:** - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `email`, `role`, `status` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: - 429: ## Organization - Billing ### GET /api/organizations/{organizationId}/billing/payment-methods **List Organization Payment Methods** Get a list of payment methods associated with this organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/organizations/{organizationId}/billing/payment-methods **Associate Payment Methods to Organization** Associate one or more payment methods to an organization. The payment methods must belong to the authenticated user. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `payment_method_ids`: string[] (required) — Array of payment method IDs to associate. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `payment_methods_not_found`: One or more payment methods not found. - 422: - 429: ### DELETE /api/organizations/{organizationId}/billing/payment-methods **Detach Payment Methods from Organization** Remove the association between one or more payment methods and an organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `payment_method_ids`: string[] (required) — Array of payment method IDs to detach. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `payment_methods_not_found`: One or more payment methods not found. - 422: - 429: ### PATCH /api/organizations/{organizationId}/billing/payment-methods/{paymentMethodId}/default **Set or Unset Default Payment Method for Organization** Set or unset the default payment method for the organization credit purchases and automatic recharges. The payment method must be associated with the organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `paymentMethodId` (path): string (required) — Id of payment method **Request Body:** Content-Type: application/json - `is_default`: boolean — Whether to set this payment method as default (true) or unset it as default (false). **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `payment_method_not_in_organization`: Payment method is not associated with this organization. - `validation_failed`: The request failed validation. - 429: ### GET /api/organizations/{organizationId}/billing/invoices **List Organization Invoices** Get a list of invoices for this organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## Organization - Projects ### GET /api/organizations/{organizationId}/projects **List Organization's Projects** Get a paginated list of all projects in an organization. Pagination parameters are optional. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `admin`, `description`, `last_activity_at`, `organization_name`, `title`, `user_joined_at` **Default order:** `admin:desc`, `last_activity_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `admin`, `organization_name`, `title`, `description` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## Organization - Reports ### GET /api/organizations/{organizationId}/reports/activity **Get Organization Activity Report** Get the activity for an specific organization **Parameters:** - `organizationId` (path): string (required) — Id of organization - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `action`, `date` **Default order:** `date:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Request Body:** Content-Type: application/json - `start_date`: string — Start date to filter by - `end_date`: string — End date to filter by - `filter_by`: string[] **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/organizations/{organizationId}/reports/activity-summary **Get Organization Activity Summary** Get the activity summary for an specific organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `start_date`: string — Start date to filter by - `end_date`: string — End date to filter by - `filter_by`: string[] **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/organizations/{organizationId}/reports/machine-translation-transactions-summary **Get Organization Machine Translation Usage Summary** Get machine translation transactions summary for an specific organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/organizations/{organizationId}/users/{userId}/reports/activity **Get User Activity By Day** Get the activity for an specific user. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `userId` (path): string (required) — Id of user. - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `action`, `date` **Default order:** `date:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/organizations/{organizationId}/users/{userId}/reports/activity-summary **Get User Activity Summary** Get the activity summary for an specific user. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `userId` (path): string (required) — Id of user. - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## Organization - Settings ### PATCH /api/organizations/{organizationId}/settings **Set Organization Settings** Set the settings for an organization. When the request adds target locales, the response also carries `target_locale_changes` — every project that gained them, the words and exact cost of machine translating their existing content, and whether the user must be asked first — plus an `agent_context` explaining it. Nothing is translated unless the organization's (or the project's own) `ai_translator.machine_translate_added_target_locales` setting is `always`. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `locales`: object - `ai_translator`: object - `billing`: object **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/organizations/{organizationId}/locale-settings **Get Organization Locale Settings** Get the locale settings for an organization. Will return an empty object if no custom settings are set for the organization. Empty object means the organization is using the system's default locale settings. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/organizations/{organizationId}/locale-settings **Clear Organization Locale Settings** Clear the locale settings for an organization. Will clear the locale settings for the organization, effectively setting it back to the default locale settings. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/organizations/{organizationId}/ai-translator-settings **Get Organization AI Translator Settings** Get the AI translator settings for an organization. Will return an empty object if no custom settings are set for the organization. Empty object means the organization is using the system's default AI translator settings. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/organizations/{organizationId}/ai-translator-settings **Clear Organization AI Translator Settings** Clear the AI translator settings for an organization. Will clear the AI translator settings for the organization, effectively setting it back to the default AI translator settings. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/organizations/{organizationId}/billing-settings **Get Organization Billing Settings** Get the billing settings for an organization. Will return an empty object if no custom settings are set for the organization. Empty object means the organization is using the system's default billing settings. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/organizations/{organizationId}/billing-settings **Clear Organization Billing Settings** Clear the billing settings for an organization. Will clear the billing settings for the organization, effectively setting it back to the default billing settings. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## Organization - Users ### GET /api/organizations/{organizationId}/users **List Organization Users** Get a list of users who have access to this organization. Pagination parameters are optional. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `role` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/organizations/{organizationId}/users/settings **Get User Organization Settings** Get overridden user settings for an organization. Will return an empty object if no settings are overridden for the user in the organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/organizations/{organizationId}/users/settings **Clear User Organization Settings** Clear overridden user settings for an organization. Will do nothing if no overriden settings exist. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### PATCH /api/organizations/{organizationId}/users/settings **Update User's Organization Settings** Update authenticated user settings for the given organization. **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `notifications`: object — The user notification settings. Available channels: broadcast, mail. Broadcast should be used to send in-app notifications to the user; mail should be used to send email notifications to the user. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### POST /api/organizations/{organizationId}/users/{userId} **Add User to Organization** Add a user to an organization. This endpoint activates the user immediately. Only sends a notification email, it does not follow the invitation workflow.

**This function will send a notification email to user. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `userId` (path): string (required) — Id of user. **Request Body:** Content-Type: application/json - `role`: "organization_admin" | "organization_user" | "billing_manager" — Role of the user in the organization - `disabled_projects`: string[] — A list of the ids of projects that will be disabled for the user. This list will sync with the currently disabled projects of the user. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `translator_limit_reached`: Your plan's translator limit is {limit}. - `user_limit_reached`: Your plan's user limit is {limit}. - `validation_failed`: The request failed validation. Possible validation errors: - `role`.`already_member`: The user is already part of this organization. - `role`.`already_member`: The user is already part of this project. - `role`.`not_member`: The user is not part of this organization. - `role`.`not_member`: The user is not part of this project. - `disabled_projects`.`invalid_option`: The list of disabled projects is invalid. - 429: ### DELETE /api/organizations/{organizationId}/users/{userId} **Remove User from Organization** Remove user from organization. Only available for organization admins. Only the organization owner can remove an admin. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `userId` (path): string (required) — Id of user. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### PATCH /api/organizations/{organizationId}/users/{userId} **Update User's Organization Access** Edit user role and project access to an organization. A user will be authorized to edit another if requester is super admin, organization owner, or the user who invited the target user. **Parameters:** - `organizationId` (path): string (required) — Id of organization - `userId` (path): string (required) — Id of user. **Request Body:** Content-Type: application/json - `role`: "organization_admin" | "organization_user" | "billing_manager" — Role of the user in the organization - `disabled_projects`: string[] — A list of the ids of projects that will be disabled for the user. This list will sync with the currently disabled projects of the user. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `role`.`already_member`: The user is already part of this organization. - `role`.`already_member`: The user is already part of this project. - `role`.`not_member`: The user is not part of this organization. - `role`.`not_member`: The user is not part of this project. - `disabled_projects`.`invalid_option`: The list of disabled projects is invalid. - 429: ## Project - Parked Work ### GET /api/projects/{projectId}/parked-work **Get work waiting on credit** Returns everything the credits gate is holding for this project: pages waiting to be checked for new content, phrases waiting to be machine translated, and the exact cost of translating them. Machine translation is priced per source word, so that figure is the amount that will be charged. Page renders carry no figure — how much content a page holds is only known once it has been loaded. This work runs automatically as soon as the project has credit to cover it. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/projects/{projectId}/parked-work **Discard work waiting on credit** Throws away the backlog so it will not run when the project is next topped up. Nothing is charged. Registered phrases keep being served in their original language and the pages are simply no longer queued to be checked. New work parks normally from here on. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## Billing - Payment Methods ### GET /api/billing/payment-methods **Get credit cards belonging to user** Fetch all available credit cards for the authenticated user. Can be filtered by organization or project. **Parameters:** - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `cc_brand`, `cc_month`, `cc_name`, `cc_year`, `default` **Default order:** `default:desc`, `cc_brand:asc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### POST /api/billing/payment-methods **Add a new credit card** Add a new credit card for the authenticated user. **Request Body:** Content-Type: application/json - `cc_number`: string (required) — Full credit card number. - `cc_name`: string (required) — Cardholder name as it appears on the card. - `cc_month`: string (required) — Card expiration month (2 digits). - `cc_year`: string (required) — Card expiration year (4 digits). - `alias`: string — Alias/nickname for this payment method. - `auto_generate_alias`: boolean — Auto-generate alias if not provided. - `cc_cvv`: string — Card Verification Value - the 3-digit security code on the back of most cards (4 digits on front for American Express). - `country_code`: string — Two-letter ISO country code where the card was issued or the billing address is located. - `billing_address`: object — Billing address information - `is_default`: boolean — Set this card as the default payment method. - `user_billing_address_id`: string — ID of user billing address to use for this credit card - `cc_brand`: string **Responses:** - 200: Success - 401: - 402: - 403: - 409: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `cc_number`.`already_taken`: This credit card has already been added. - `cc_month`.`invalid_date`: The expiration date is invalid. - `cc_month`.`expired`: The credit card is expired. - `billing_address.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `billing_address.vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - `user_billing_address_id`.`not_found`: The selected billing address is invalid. - `cc_number`.`invalid_format`: This card number is not valid. - `billing_address.state`.`invalid_option`: The state in the billing address is not valid. - `user_billing_address_id`.`invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 503: ### GET /api/billing/payment-methods/{paymentMethodId} **Get Credit Card** Get credit card belonging to user **Parameters:** - `paymentMethodId` (path): string (required) — Id of payment method **Responses:** - 200: Success - 401: - 403: - 404: Possible errors: - `not_found`: Resource not found - `payment_method_not_found`: Payment method not found. - 429: ### DELETE /api/billing/payment-methods/{paymentMethodId} **Delete a credit card** Removes a saved card from the authenticated user's account and from every organization and project that used it. Charges already made with it can still be refunded. **Parameters:** - `paymentMethodId` (path): string (required) — ID of the credit card **Responses:** - 200: Success - 401: - 403: - 404: Possible errors: - `not_found`: Resource not found - `payment_method_not_found`: Payment method not found. - 422: - 429: ### PATCH /api/billing/payment-methods/{paymentMethodId} **Update a credit card** Update an existing credit card for the authenticated user. **Parameters:** - `paymentMethodId` (path): string (required) — Id of payment method **Request Body:** Content-Type: application/json - `cc_name`: string — Cardholder name as it appears on the card. - `cc_month`: string — Card expiration month (2 digits). - `cc_year`: string — Card expiration year (4 digits). - `alias`: string — Alias/nickname for this payment method. - `auto_generate_alias`: boolean — Auto-generate alias if not provided. - `cc_cvv`: string — Card Verification Value - the 3-digit security code on the back of most cards (4 digits on front for American Express). - `country_code`: string — Two-letter ISO country code where the card was issued or the billing address is located. - `billing_address`: object — Billing address information - `is_default`: boolean — Set this card as the default payment method. - `user_billing_address_id`: string — ID of user billing address to use for this credit card **Responses:** - 200: Success - 401: - 402: - 403: - 404: Possible errors: - `not_found`: Resource not found - `payment_method_not_found`: Payment method not found. - 409: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `cc_month`.`invalid_date`: The expiration date is invalid. - `cc_month`.`expired`: The credit card is expired. - `billing_address.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `billing_address.vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - `user_billing_address_id`.`not_found`: The selected billing address is invalid. - `billing_address.state`.`invalid_option`: The state in the billing address is not valid. - `user_billing_address_id`.`invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 503: ### PATCH /api/billing/payment-methods/{paymentMethodId}/default **Set or Unset Default Payment Method for User** Set or unset the default payment method for the authenticated user. **Parameters:** - `paymentMethodId` (path): string (required) — ID of the payment method to set as default **Request Body:** Content-Type: application/json - `is_default`: boolean — Whether to set this payment method as default (true) or unset it as default (false). **Responses:** - 200: Success - 401: - 403: - 404: Possible errors: - `not_found`: Resource not found - `payment_method_not_found`: Payment method not found. - 422: - 429: ## Plans ### GET /api/plans/{plan_type}/{plan_cycle} **Get plan details** Get the pricing row for a given plan type and billing cycle. **Parameters:** - `plan_type` (path): string (required) - `plan_cycle` (path): string (required) **Responses:** - 200: Success - 401: - 403: - 404: - 429: ## Project - Credits ### GET /api/projects/{projectId}/credits/transactions **Get Project Credit Transactions** Get the credit balance of a project. **Parameters:** - `projectId` (path): string (required) — Id of project - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `amount`, `created_at`, `type` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `type` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/projects/{projectId}/credits **Get Project Credit** Get the credit of a project. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/projects/{projectId}/credits **Add Credit to Project** Add credit to a project's balance. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `amount`: number (required) — Amount of credit to add. - `cc_id`: string — The credit card id for the existing credit card - `credit_card`: object — The credit card data for the new credit card - `save_credit_card`: boolean — Keep the card typed in credit_card for later payments. Off unless sent. A card already saved stays saved either way. - `use_subscription_billing_address`: boolean — Whether to use the subscription billing address in the invoice, if false, the payment method address will be used if user_organization_address is false - `use_organization_address`: boolean — Whether to use the organization address in the invoice. This will only apply when adding credit to a specific organization or project - `user_billing_address_id`: string — ID of user billing address to use for this transaction. Optional: without one, tax follows the organization's country and VAT ID. - `payment_provider`: string **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 401: - 402: Possible errors: - `no_valid_payment_method`: No default payment method found for the entity. - `payment_method_rejected`: This card was declined. Please use a different card or contact your bank. - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `validation_failed`: The request failed validation. Possible validation errors: - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `user_billing_address_id`.`not_found`: The selected billing address is invalid. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 502: - 503: ### POST /api/projects/{projectId}/credits/transfer/organization/{organizationId} **Transfer Credits from Project to Organization** **Parameters:** - `projectId` (path): string (required) — Id of project - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `prepaid_credits`: number — Amount of prepaid credits to transfer. - `free_credits`: number — Amount of free credits to transfer. **Responses:** - 200: Success - 401: - 402: Possible errors: - `insufficient_balance`: Insufficient balance to process request - `insufficient_free_credits`: Insufficient free credits to process request - `insufficient_prepaid_credits`: Insufficient prepaid credits to process request - `monthly_usage_limit_exceeded`: Monthly credit usage limit exceeded - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `project_not_in_organization`: Project does not belong to the specified organization. - 422: `validation_failed`: The request failed validation. Possible validation errors: - `prepaid_credits`.`required`: Either the prepaid credits or the free credits must be greater than 0. - 429: ### GET /api/projects/{projectId}/credits/summary **Get Project Credit Usage Summary** Get the aggregated credit usage summary of a project, optionally filtered by date range. **Parameters:** - `projectId` (path): string (required) — Id of project - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## Project ### GET /api/projects **List User's Projects** Get all projects a given user has access to. Super admins will view all projects. Pagination parameters are optional. If user locale is sent it will return project target locales as an object categorized by language and displayed in the language of {userLocale}; otherwise it will only return a plain array of locale codes. Results can be ordered by: title, description, organization_name, admin, last_activity_at, user_joined_at, created_at, updated_at, id. **Parameters:** - `visitor_locale` (query): string — Query parameter representing locale code to display the data in. - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `admin`, `organization_name`, `title`, `description` **Responses:** - 200: Success - 401: - 403: - 429: ### POST /api/projects **Create New Project** Create a project in an organization. **Request Body:** Content-Type: application/json - `organization_id`: string (required) - `title`: string (required) - `description`: string - `website_url`: string - `icon`: object — Favicon of the project. If not provided, Langsys will attempt to fetch it from the website URL - `logo`: object — Logo of the project. If not provided, Langsys will attempt to fetch it from the website URL - `settings`: object — If the project does not have these settings set specifically for it, then its organization settings will be used instead. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `locale_limit_reached`: Your plan's locale limit is {limit}. - `project_limit_reached`: Your plan's project limit is {limit}. - `validation_failed`: The request failed validation. - 429: ### GET /api/projects/{projectId} **Get Project Details** Get a single project. If user locale is sent it will return project target locales as an object categorized by language and displayed in the language of {userLocale}; otherwise it will only return a plain array of locale codes. **Parameters:** - `projectId` (path): string (required) — Id of project - `visitor_locale` (query): string — Query parameter representing locale code to display the data in. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/projects/{projectId} **Delete Project** Delete a project. Optionally transfer all phrases and translations to another project before deletion. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `target_project_id`: string — Project ID to transfer phrases to before deletion. If not provided, phrases will be deleted. - `include_translations`: boolean — Whether to also transfer translations. - `create_target_locales`: boolean — Whether to create target locales in target project if they do not exist. If false, only translations to existing locales will be transferred. - `force_transfer`: boolean — Force transfer even if projects base locale does not match. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 409: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `target_project_id`.`not_found`: The target project is invalid. - `target_project_id`.`mismatch`: The source and target projects must have the same base locale unless force_transfer is enabled. - 429: ### PATCH /api/projects/{projectId} **Update Project** Update a project. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `organization_id`: string - `title`: string - `description`: string - `website_url`: string - `icon`: object - `logo`: object - `settings`: object **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `locale_limit_reached`: Your plan's locale limit is {limit}. - `validation_failed`: The request failed validation. - 429: ### GET /api/projects/{projectId}/stats **Get Project Statistics** Get a detailed stats about a project's phrases and translations. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### PATCH /api/projects/{projectId}/default **Set Default Language Locale** Set locale code as default project target locale for that language. This will unset any previous default and will add the locale in case it didn't exist as a project target locale. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `locale`: string (required) — Translation locale. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `locale_limit_reached`: Your plan's locale limit is {limit}. - `validation_failed`: The request failed validation. - 429: ### POST /api/projects/{projectId}/transfer-ownership **Transfer Project Ownership** Transfer ownership of the project to a new user. Only available for project owner or organization admins. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `user_id`: string — The id of the user to invite. If this field is provided, then the email field will be ignored. - `email`: string — This field should be provided if the userId is not provided. It should be used for users who are not Langsys users. If the email provided is for an existing user then this will behave in the same way as sending the userId. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `user_id`.`required`: The user is required when email is not present. - `email`.`already_member`: The user is already part of this organization. - `email`.`already_member`: The user is already part of this project. - `email`.`already_owner`: The user is already the owner. - 429: ## Project - Invitations ### GET /api/projects/{projectId}/invitations **List Project Invitations** Get all sent invitations for a project. Only available for project admins. **Parameters:** - `projectId` (path): string (required) — Id of project - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `email`, `role`, `status` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/projects/{projectId}/invitations **Invite User to Project** Invite a user to a project.

**This function will send an invitation email to user. An invitation takes a seat as soon as it is sent: one that would take the organization past its plan user limit, counting live invitations, answers 422 user_limit_reached. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `user_id`: string — The id of the user to invite. If this field is provided, then the email field will be ignored. - `email`: string — The email of the user to invite. Should be provided if the user is not part of the Langsys platform yet. If the email provided is for an existing user then this will behave in the same way as sending that user_id in the request - `role`: "project_admin" | "project_user" | "translator" | "billing_manager" - `target_locales`: string[] — List of locales translator can translate to. Must be target locales of the project. Only send this if role is of type translator. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `email_undeliverable`: The email address could not be verified. - `translator_limit_reached`: Your plan's translator limit is {limit}. - `user_limit_reached`: Your plan's user limit is {limit}. - `validation_failed`: The request failed validation. Possible validation errors: - `user_id`.`required`: The user is required when email is not present. - `email`.`already_member`: The user is already part of this organization. - `email`.`already_member`: The user is already part of this project. - `email`.`already_owner`: The user is already the owner. - `target_locales`.`required`: The target locales is required when the role is translator. - `target_locales`.`invalid_type`: The target locales must be a list of locale codes. - `target_locales`.`invalid_option`: These locales are not target locales of this project: {locales}. - 429: ### GET /api/projects/invitations/all **List All Project Invitations** Retrieve all project invitations that have been sent where the logged user holds an admin role. **Parameters:** - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `email`, `role`, `status` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: - 429: ## Project - Billing ### GET /api/projects/{projectId}/billing/payment-methods **List Project Payment Methods** Get a list of payment methods associated with this project. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/projects/{projectId}/billing/payment-methods **Associate Payment Methods to Project** Associate one or more payment methods to a project. The payment methods must belong to the authenticated user. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `payment_method_ids`: string[] (required) — Array of payment method IDs to associate. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `payment_methods_not_found`: One or more payment methods not found. - 422: - 429: ### DELETE /api/projects/{projectId}/billing/payment-methods **Detach Payment Methods from Project** Remove the association between one or more payment methods and a project. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `payment_method_ids`: string[] (required) — Array of payment method IDs to detach. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: Possible errors: - `not_found`: Resource not found - `payment_methods_not_found`: One or more payment methods not found. - 422: - 429: ### PATCH /api/projects/{projectId}/billing/payment-methods/{paymentMethodId}/default **Set or Unset Default Payment Method for Project** Set or unset the default payment method for the project credit purchases and automatic recharges. The payment method must be associated with the project. **Parameters:** - `projectId` (path): string (required) — Id of project - `paymentMethodId` (path): string (required) — Id of payment method **Request Body:** Content-Type: application/json - `is_default`: boolean — Whether to set this payment method as default (true) or unset it as default (false). **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `payment_method_not_in_project`: Payment method is not associated with this project. - `validation_failed`: The request failed validation. - 429: ### GET /api/projects/{projectId}/billing/invoices **List Project Invoices** Get a list of invoices for this project. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## Project - Reports ### GET /api/projects/{projectId}/reports/machine-translation-transactions **Get Project Translation Transactions** Get machine translation transactions for a specific project. Optionally filter by user ID, locale code, machine translator and date range. **Parameters:** - `projectId` (path): string (required) — Id of project - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `amount`, `created_at`, `translator` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### GET /api/projects/{projectId}/reports/machine-translation-transactions-summary **Get Project Translation Summary** Get machine translation transactions summary for a specific project. Optionally filter by date range. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `start_date`: string — Start date to filter by - `end_date`: string — End date to filter by - `filter_by`: string[] **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## Project - Settings ### PATCH /api/projects/{projectId}/settings **Set Project Settings** Set the settings for a project. When the request adds target locales, the response also carries `target_locale_changes` — the words and exact cost of machine translating the project's existing content into them — plus an `agent_context` explaining it. Nothing is translated unless `ai_translator.machine_translate_added_target_locales` is `always`. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `locales`: object - `ai_translator`: object - `billing`: object **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `locale_limit_reached`: Your plan's locale limit is {limit}. - `validation_failed`: The request failed validation. - 429: ### GET /api/projects/{projectId}/locale-settings **Get Project Locale Settings** Get the override locale settings for a project. Will return an empty object if no settings are set for the project. Empty object means that the project is inheriting its locale settings from the organization. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/projects/{projectId}/locale-settings **Clear Project Locale Settings Override** Clear the override locale settings for a project, setting it back to its organization's locale settings. Re-attaching usually GAINS target locales — every locale the organization has that the project did not — so the response carries the same `target_locale_changes` and `agent_context` a settings PATCH returns: what machine translating the existing content into them would cost, and whether it was translated or is waiting on the user. Nothing is translated unless `ai_translator.machine_translate_added_target_locales` is `always`. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/projects/{projectId}/ai-translator-settings **Get Project AI Translator Settings** Get the override AI translator settings for a project. This will return the AI translator settings that are overridden for the project or an empty object if no settings are overridden for the project. Empty object means that the project is inheriting its AI translator settings from the organization. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/projects/{projectId}/ai-translator-settings **Clear Project AI Translator Settings Override** Clear the override AI translator settings for a project. This will clear the AI translator settings that are overridden for the project, effectively setting it back to its organization's AI translator settings. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/projects/{projectId}/billing-settings **Get Project Billing Settings** Get the override billing settings for a project. This will return the billing settings that are overridden for the project or an empty object if no settings are overridden for the project. Empty object means that the project is inheriting its billing settings from the organization. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/projects/{projectId}/billing-settings **Clear Project Billing Settings Override** Clear the override billing settings for a project. This will clear the billing settings that are overridden for the project, effectively setting it back to its organization's billing settings. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ## Project - Trash ### GET /api/projects/trash **View deleted projects** List deleted projects within the last 45 days. Optionally filter by organization. If no organization is provided, all deleted projects from organizations where the user holds an admin role will be returned. **Parameters:** - `organization_id` (query): string — Filter results by organization ID - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `admin`, `description`, `last_activity_at`, `organization_name`, `title`, `user_joined_at` **Default order:** `admin:desc`, `last_activity_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `admin`, `organization_name`, `title`, `description` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### POST /api/projects/restore **Restore deleted projects** Restore projects that have been deleted within the last 45 days. If the user does not have permission to restore one or more of the projects in the list, they will simply be excluded and not restored. **Request Body:** Content-Type: application/json - `restoreable_ids`: string[] (required) **Responses:** - 200: Success - 401: - 403: - 422: - 429: ## Project - Users ### GET /api/projects/{projectId}/users **Get Project Users** Get a list of users who have access to this project. Pagination parameters are optional. **Parameters:** - `projectId` (path): string (required) — Id of project - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `created_at`, `role` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### GET /api/projects/{projectId}/users/settings **Get User Project Settings** Get overridden user settings for a project. Will return an empty object if no settings are overridden for the user in the project. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### DELETE /api/projects/{projectId}/users/settings **Clear User Project Settings** Clear overridden user settings for a project. Will do nothing if no overriden settings exist. **Parameters:** - `projectId` (path): string (required) — Id of project **Responses:** - 200: success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### PATCH /api/projects/{projectId}/users/settings **Update User Project Settings** Update authenticated user settings for a project. **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `notifications`: object — The user notification settings. Available channels: broadcast, mail. Broadcast should be used to send in-app notifications to the user; mail should be used to send email notifications to the user. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### POST /api/projects/{projectId}/users/{userId} **Add User to Project** Add a user to a project. This endpoint activates the user immediately. Only sends a notification email, it does not follow the invitation workflow. User must be part of the organization in order to be added directly to a project. **Parameters:** - `projectId` (path): string (required) — Id of project - `userId` (path): string (required) — Id of user. **Request Body:** Content-Type: application/json - `role`: "project_admin" | "project_user" | "translator" | "billing_manager" - `target_locales`: string[] — List of locales translator can translate to. Only send this if role is of type translator. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: Possible errors: - `translator_limit_reached`: Your plan's translator limit is {limit}. - `user_limit_reached`: Your plan's user limit is {limit}. - `validation_failed`: The request failed validation. Possible validation errors: - `role`.`already_member`: The user is already part of this organization. - `role`.`already_member`: The user is already part of this project. - `role`.`not_member`: The user is not part of this organization. - `role`.`not_member`: The user is not part of this project. - `target_locales`.`required`: The target locales is required when the role is translator. - `target_locales`.`invalid_type`: The target locales must be a list of locale codes. - `target_locales`.`invalid_option`: These locales are not target locales of this project: {locales}. - 429: ### DELETE /api/projects/{projectId}/users/{userId} **Remove User From Project** Remove user from project. Only available for project admins. Only the project owner can remove an admin. If user to be removed is an organization admin, then the project will be added to the user's disabled projects. **Parameters:** - `projectId` (path): string (required) — Id of project - `userId` (path): string (required) — Id of user. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### PATCH /api/projects/{projectId}/users/{userId} **Update User Project Access** Change the role of a user in an project. Only available for project admins. Only the project owner or an organization admin can change the role of an admin. **Parameters:** - `projectId` (path): string (required) — Id of project - `userId` (path): string (required) — Id of user. **Request Body:** Content-Type: application/json - `role`: "project_admin" | "project_user" | "translator" | "billing_manager" - `target_locales`: string[] — List of locales translator can translate to. Only send this if role is of type translator. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `role`.`already_member`: The user is already part of this organization. - `role`.`already_member`: The user is already part of this project. - `role`.`not_member`: The user is not part of this organization. - `role`.`not_member`: The user is not part of this project. - `target_locales`.`required`: The target locales is required when the role is translator. - `target_locales`.`invalid_type`: The target locales must be a list of locale codes. - `target_locales`.`invalid_option`: These locales are not target locales of this project: {locales}. - 429: ## Translatable Items ### GET /api/translatable-items/{translatableItemId} **Get translatable item** Get a single phrase or a content block. **Parameters:** - `translatableItemId` (path): string (required) — Translatable item ID to delete - `locale` (query): string — Locale to get the translations for. If not locale if provided, project base locale will be used. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### POST /api/translatable-items **Create or update translatable items** Create a single or a list of translatable items. If a phrase that already exists is provided, the translatable attribute will be updated if it is different from the existing value. If the custom_id provided for content block already exists, the content block will be updated; this also means phrases, their translatable attributes, and their order will be updated. **Request Body:** Content-Type: application/json - `project_id`: string (required) - `translatable_items`: object[] (required) - `custom_id`: string **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: Possible errors: - `batch_size_exceeded`: The request has more translatable items than one batch allows. - `validation_failed`: The request failed validation. Possible validation errors: - `translatable_items.0.phrase`.`invalid_format`: The phrase must not contain null bytes. - `translatable_items.0.category`.`invalid_format`: The category must not contain null bytes. - `translatable_items.0.phrases.0.phrase`.`invalid_format`: The phrase must not contain null bytes. - `translatable_items.0.phrases.0.translations.0.translation`.`invalid_format`: The translation must not contain null bytes. - `translatable_items.0.custom_id`.`invalid_format`: The custom ID must not contain null bytes. - `translatable_items.0.content`.`invalid_format`: The content must not contain null bytes. - `translatable_items.0.label`.`invalid_format`: The label must not contain null bytes. - `translatable_items.0.translations.0.translation`.`invalid_format`: The translation must not contain null bytes. - `translatable_items.*.phrases.*.translations.*.locale`.`invalid_option`: The locale {locale} is not a target locale of this project. - `translatable_items.*.translations.*.locale`.`invalid_option`: The locale {locale} is not a target locale of this project. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### DELETE /api/translatable-items **Delete translatable items** Delete one or multiple translatable items by providing an array of IDs in the request body. To delete a single item, send an array with one ID. All items must belong to the same project. Optional flags allow deletion of associated translations and memory translations. **Request Body:** Content-Type: application/json - `translatable_item_ids`: string[] — Array of translatable item IDs to delete - `delete_translations`: boolean — Whether to also delete translations - `delete_memory_translations`: boolean — Whether to also delete memory translations **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `translatable_item_ids`.`required`: At least one translatable item is required. - `translatable_item_ids`.`not_found`: None of the translatable items were found. - `translatable_item_ids`.`invalid_option`: All translatable items must belong to the same project. - 429: ### POST /api/translatable-items/transfer **Transfer Translatable Items** Transfer all translatable items (phrases and content blocks) from one project to another. This includes standalone phrases and content blocks with their associated phrases. **Request Body:** Content-Type: application/json - `source_project_id`: string (required) - `target_project_id`: string (required) - `include_translations`: boolean — If true, all translations for the project will be transferred, and all memory translations that are realted to the project phrases will be transferred as well. - `create_target_locales`: boolean — Whether to create target locales in target project if they do not exist. If false, only translations to existing locales will be transferred. - `force_transfer`: boolean — Force transfer even if projects base locale does not match. - `transfer_mode`: "copy" | "move" — Transfer mode: copy (default) keeps items in source project, move removes them from source project. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 422: `validation_failed`: The request failed validation. Possible validation errors: - `target_project_id`.`not_found`: The target project is invalid. - `target_project_id`.`mismatch`: The source and target projects must have the same base locale unless force_transfer is enabled. - 429: ### DELETE /api/translatable-items/all **Delete all translatable items in a project** Delete all translatable items (phrases and content blocks) in a project. All translations and content blocks associated with the project will moved to the trash. Optional flags allow deletion of associated translations and memory translations. **Request Body:** Content-Type: application/json - `project_id`: string (required) — Project ID to delete all translatable items from - `delete_translations`: boolean — Whether to also delete translations - `delete_memory_translations`: boolean — Whether to also delete memory translations **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## Translatable Items - Trash ### GET /api/translatable-items/trash **Get Deleted Translatable Items** Get deleted translatable items from a project. **Parameters:** - `project_id` (query): string (required) — Project ID to get deleted items from **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### POST /api/translatable-items/restore **Restore Deleted Translatable Items** Restore deleted translatable items from a project. **Request Body:** Content-Type: application/json - `project_id`: string (required) - `restoreable_ids`: string[] (required) - `restore_translations`: boolean - `restore_memory_translations`: boolean **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### POST /api/translatable-items/restore/all **Restore All Deleted Translatable Items** Restore all deleted translatable items from a project. **Request Body:** Content-Type: application/json - `project_id`: string (required) - `restore_translations`: boolean - `restore_memory_translations`: boolean **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## Translations ### GET /api/translations **List translations in flat format** Get a list of translations. Response will be an object representing a list of translations in the form [Category] => [phrase => translation]. Content blocks will have an additional grouping by their custom_id. Translations follow the following order: [untranslated DESC, ai_translated DESC, memory_translated DESC, translatable DESC, phrase/label ASC] **Parameters:** - `project_id` (query): string (required) — Project ID to get the translations for. - `locale` (query): string (required) - `format` (query): "flat" | "data" — Response format type. When set to 'data', the response will return the schema shown in the endpoint that has this same structure plus /data at the end. When not provided or when using flat, it returns the schema described in this endpoint. **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `locale`.`invalid_option`: The locale is not valid. - `locale`.`invalid_option`: The locale is not a base or target locale of this project. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### GET /api/translations/data **List translations in data format** Get a list of translations. Translations will be grouped by category. Translations follow the following order: [untranslated DESC, ai_translated DESC, memory_translated DESC, translatable DESC, phrase/label ASC] **Parameters:** - `project_id` (query): string (required) — Project ID to get the translations for. - `locale` (query): string (required) - `format` (query): "flat" | "data" — Response format type. When set to 'data', the response will return the schema shown in the endpoint that has this same structure plus /data at the end. When not provided or when using flat, it returns the schema described in this endpoint. **Responses:** - 200: Success - 401: - 402: Possible errors: - `api_units_limit_exceeded`: Monthly API usage units limit exceeded. Upgrade your plan to continue. - `subscription_suspended`: Subscription is suspended due to non-payment. Please pay the outstanding invoice to restore access. - 403: Possible errors: - `api_key_invalid`: Invalid API key - `api_key_write_not_allowed`: This API key cannot make write requests. - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - `project_unavailable`: This project is not available. Its owners can see why in Langsys. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `locale`.`invalid_option`: The locale is not valid. - `locale`.`invalid_option`: The locale is not a base or target locale of this project. - 429: Possible errors: - `duplicate_request`: Too many identical requests. Please wait a moment before retrying. - `too_many_requests`: Too many requests. Please try again later. ### DELETE /api/translations/{translatableItemId} **Delete translations for a translatable item** Delete translations for a translatable item. **Parameters:** - `translatableItemId` (path): string (required) — Translatable item ID to delete **Request Body:** Content-Type: application/json - `locale`: string (required) — Translation locale. Must be one of the project's target locales. - `updateTM`: boolean — Whether to delete related entries from translation memory. This field is optional and false if not present. - `delete_language_locales`: boolean — If true, all translations for locales of the same language as the locale provided will be deleted as well. **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `locale`.`invalid_option`: The locale is not a target locale of this project. - `locale`.`not_allowed`: You are not assigned to translate this locale. - 429: ### PATCH /api/translations/{translatableItemId} **Create or update translations for a translatable item** Create a translation for a phrase or a list of translations for a content block. When creating translations for a content block, then a list of phrases and translations must be provided. Translation text is only required if not using the endpoint to mark a phrase as untranslatable for a locale. When marking the phrase as untranslatable, all target locales of the same language of the requested locale will be marked as untranslatable as well. This only applies for target locales the user has access to; translators will have access to target locales assigned to them, while staff users will have access to all target locales. **Parameters:** - `translatableItemId` (path): string (required) — Translatable item ID to delete **Request Body:** Content-Type: application/json - `locale`: string (required) — Translation locale. - `translation`: string — Translation in queried locale. Only required if translatable is true and request is for a single phrase; otherwise this field will be ignored. - `updateTM`: boolean — Whether to save this translation in translation memory. - `translatable`: boolean — If this flag is false, the translatable item will be marked as untranslatable for requested locale and all project target locales of the same language the requesting user has access to. - `translations`: object[] — List of translations for content block. Should be used when translatable item is a content block, unless marking all phrases inside the content block as untranslatable. In that case the translatable field sibling to this one must be set to false and this field can be left empty. **Responses:** - 200: Success - 401: - 402: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `locale`.`invalid_option`: The locale is not valid. - `locale`.`invalid_option`: The locale is not a target locale of this project. - `locale`.`not_allowed`: You are not assigned to translate this locale. - `translations.0.phrase_id`.`not_found`: The phrase is not part of this project. - `translatable`.`not_allowed`: This phrase is marked as untranslatable. - 429: ## Translations - Trash ### GET /api/translations/trash **View deleted translations** Retrieve translations that have been deleted within the last 45 days. **Parameters:** - `project_id` (query): string (required) — Project ID to get deleted translations from **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### POST /api/translations/restore **Restore a list of translations.** Restore translations that have been deleted within the last 45 days. Endpoint expects a list of translation ids, which can be retrieved from the trash endpoint. To restore translations for translatable items, use the translatable-items/restore endpoints. **Request Body:** Content-Type: application/json - `project_id`: string (required) - `restoreable_ids`: string[] (required) **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ### POST /api/translations/restore/all **Restore all translations** Restore all deleted translations for a project deleted within the last 45 days. **Request Body:** Content-Type: application/json - `project_id`: string (required) - `restore_memory_translations`: boolean **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: - 429: ## User - Credits ### GET /api/me/credits/transactions **Get User Credit Transactions** Get the credit balance of the authenticated user. **Parameters:** - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range - `page` (query): integer — Page to request - `records_per_page` (query): integer — Number of records per page - `order_by` (query): string | string[] — Order results by specified field(s). Supports single field (order_by=field:direction) or multiple fields for tie-breaking (order_by[]=field1:direction&order_by[]=field2:direction). **Orderable fields:** `amount`, `created_at`, `type` **Default order:** `created_at:desc` - `filter_by` (query): string | string[] — Filter results by field values. Operators: - `field:value` equality (strict type match) - `field:null` field is null - `field:!null` field is not null - `field::value` comparison (op ∈ `>`, `<`, `>=`, `<=`) — numeric fields; date comparison also supported when value is `YYYY-MM-DD` (e.g. `2026-01-01`) Multiple filters: `filter_by[]=field1:value&filter_by[]=field2:value` **Filterable fields:** `type` **Responses:** - 200: Success - 401: - 403: - 422: - 429: ### GET /api/me/credits **Get User Credit** Get the credit of the authenticated user. **Responses:** - 200: Success - 401: - 403: - 429: ### POST /api/me/credits **Add Credit to User** Add credit to a user's balance. **Request Body:** Content-Type: application/json - `amount`: number (required) — Amount of credit to add. - `cc_id`: string — The credit card id for the existing credit card - `credit_card`: object — The credit card data for the new credit card - `save_credit_card`: boolean — Keep the card typed in credit_card for later payments. Off unless sent. A card already saved stays saved either way. - `use_subscription_billing_address`: boolean — Whether to use the subscription billing address in the invoice, if false, the payment method address will be used if user_organization_address is false - `use_organization_address`: boolean — Whether to use the organization address in the invoice. This will only apply when adding credit to a specific organization or project - `user_billing_address_id`: string — ID of user billing address to use for this transaction. Optional: without one, tax follows the organization's country and VAT ID. - `payment_provider`: string **Responses:** - 200: Success - 202: Accepted, but the card payment is held for review: nothing it pays for is applied until the review approves it, and paying again is not needed. The same body as 200, plus `payment`. - 401: - 402: Possible errors: - `no_valid_payment_method`: No default payment method found for the entity. - `payment_method_rejected`: This card was declined. Please use a different card or contact your bank. - 403: - 422: Possible errors: - `payment_failed`: The payment was declined. Please use a different card or contact your bank. - `validation_failed`: The request failed validation. Possible validation errors: - `credit_card.cc_month`.`invalid_date`: The expiration date is invalid. - `credit_card.cc_month`.`expired`: The credit card is expired. - `credit_card.address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `user_billing_address_id`.`not_found`: The selected billing address is invalid. - `credit_card.cc_number`.`invalid_format`: This card number is not valid. - `invalid_option`: The state in the billing address is not valid. - `mismatch`: The card details could not be verified. Check the card number, expiration date and security code. - 429: - 502: - 503: ### POST /api/me/credits/transfer/project/{projectId} **Transfer Credits to Project** **Parameters:** - `projectId` (path): string (required) — Id of project **Request Body:** Content-Type: application/json - `prepaid_credits`: number — Amount of prepaid credits to transfer. - `free_credits`: number — Amount of free credits to transfer. **Responses:** - 200: Success - 401: - 402: Possible errors: - `insufficient_balance`: Insufficient balance to process request - `insufficient_free_credits`: Insufficient free credits to process request - `insufficient_prepaid_credits`: Insufficient prepaid credits to process request - `monthly_usage_limit_exceeded`: Monthly credit usage limit exceeded - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `prepaid_credits`.`required`: Either the prepaid credits or the free credits must be greater than 0. - 429: ### POST /api/me/credits/transfer/organization/{organizationId} **Transfer Credits to Organization** **Parameters:** - `organizationId` (path): string (required) — Id of organization **Request Body:** Content-Type: application/json - `prepaid_credits`: number — Amount of prepaid credits to transfer. - `free_credits`: number — Amount of free credits to transfer. **Responses:** - 200: Success - 401: - 402: Possible errors: - `insufficient_balance`: Insufficient balance to process request - `insufficient_free_credits`: Insufficient free credits to process request - `insufficient_prepaid_credits`: Insufficient prepaid credits to process request - `monthly_usage_limit_exceeded`: Monthly credit usage limit exceeded - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `prepaid_credits`.`required`: Either the prepaid credits or the free credits must be greater than 0. - 429: ### GET /api/me/credits/summary **Get User Credit Usage Summary** Get the aggregated credit usage summary of the authenticated user, optionally filtered by date range. **Parameters:** - `start_date` (query): string — Start date for the activity range - `end_date` (query): string — End date for the activity range **Responses:** - 200: Success - 401: - 403: - 422: - 429: ## Billing - Billing Addresses ### GET /api/billing/billing-addresses **Get user billing addresses** Fetch all billing addresses for the authenticated user. **Responses:** - 200: Success - 401: - 403: - 429: ### POST /api/billing/billing-addresses **Create a new billing address** Create a new billing address for the authenticated user. **Request Body:** Content-Type: application/json - `country_code`: string (required) — Country code - `name`: string — Name for this billing address - `address_1`: string — Primary billing address line - `address_2`: string — Secondary billing address line - `city`: string — City - `state`: string — State/Province - `zip`: string — ZIP/Postal code - `vat_id`: string — VAT/Tax registration ID (e.g. EU VAT number) - `is_default`: boolean — Set as default billing address **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 409: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - 429: ### GET /api/billing/billing-addresses/{billingAddressId} **Get a specific billing address** Get a specific billing address belonging to the authenticated user. **Parameters:** - `billingAddressId` (path): string (required) — Id of user billing address **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: ### PUT /api/billing/billing-addresses/{billingAddressId} **Update a billing address** Update an existing billing address for the authenticated user. **Parameters:** - `billingAddressId` (path): string (required) — Id of user billing address **Request Body:** Content-Type: application/json - `country_code`: string (required) — Country code - `name`: string — Name for this billing address - `address_1`: string — Primary billing address line - `address_2`: string — Secondary billing address line - `city`: string — City - `state`: string — State/Province - `zip`: string — ZIP/Postal code - `vat_id`: string — VAT/Tax registration ID (e.g. EU VAT number) - `is_default`: boolean — Set as default billing address **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 409: - 422: `validation_failed`: The request failed validation. Possible validation errors: - `address_2`.`too_long`: Together, the two address lines must not be longer than 58 characters. - `vat_id`.`invalid_format`: The VAT ID format is invalid for the selected country. - 429: ### DELETE /api/billing/billing-addresses/{billingAddressId} **Delete a billing address** Delete a billing address for the authenticated user. **Parameters:** - `billingAddressId` (path): string (required) — Id of user billing address **Responses:** - 200: Success - 401: - 403: Possible errors: - `device_mismatch`: The x-device-id header does not match the device this token was issued to. - `forbidden`: Forbidden. - 404: - 429: