Errors
Errors use conventional HTTP status codes and always have the same body:
{ "error": { "code": "expiry_too_long", "message": "The expiration cannot exceed 90 days", "details": { "maxDays": 90 } }}| Field | Description |
|---|---|
code |
Stable, machine-readable, snake_case. Branch on it. New codes may be added over time. |
message |
Human-readable explanation in English, for logs. It may change: do not parse it. |
details |
Optional object with context (e.g. issues of a validation error, orgId of consent_required). |
Status codes
Section titled “Status codes”| Status | Meaning | Retry? |
|---|---|---|
400 |
The request does not match the schema: invalid_request, with details.issues. |
No, fix the request. |
401 |
Missing, invalid or expired credentials. | Once, with a fresh token. |
403 |
Authenticated but not allowed: missing permission, feature disabled, quota reached, IP not allowed… | No. |
404 |
The resource does not exist or is not visible to the caller. | No. |
409 |
Conflict with the current state (e.g. canceling a completed request). | Re-read the resource first. |
422 |
Business rule violation (e.g. a signer phone is required for SMS codes). | No, fix the request. |
429 |
Rate limit exceeded: rate_limited. |
Yes, after Retry-After seconds. |
5xx |
Unexpected error (internal_error) or unavailability. |
Yes, with exponential backoff. |
Validation errors
Section titled “Validation errors”A 400 invalid_request lists every problem found in the body, query or path:
{ "error": { "code": "invalid_request", "message": "Invalid request", "details": { "issues": [ { "code": "invalid_type", "path": ["signers", 0, "email"], "message": "Invalid input: expected string, received undefined" }, { "code": "too_big", "path": ["title"], "message": "Too big: expected string to have <=200 characters" } ] } }}Handle errors
Section titled “Handle errors”curl -s -w '\n%{http_code}\n' "$MLT_API_URL/v1/signature-requests/0192f3a1-7c2e-7b4d-9a61-400000000999" \ -H "Authorization: Bearer $MLT_TOKEN"# {"error":{"code":"signature_request_not_found","message":"signature_request not found","details":{"id":"…"}}}# 404export class MyClmError extends Error { constructor(readonly status: number, readonly code: string, message: string, readonly details?: Record<string, unknown>) { super(message); }}
export async function call<T>(path: string, init: RequestInit = {}): Promise<T> { const response = await fetch(`${process.env.MLT_API_URL}${path}`, { ...init, headers: { Authorization: `Bearer ${token}`, ...init.headers }, }); if (response.ok) return (response.status === 204 ? undefined : await response.json()) as T; const { error } = (await response.json()) as { error: { code: string; message: string; details?: Record<string, unknown> } }; throw new MyClmError(response.status, error.code, error.message, error.details);}class MyClmError(Exception): def __init__(self, status: int, code: str, message: str, details: dict | None = None): super().__init__(f"{status} {code}: {message}") self.status, self.code, self.details = status, code, details or {}
def call(method: str, path: str, **kwargs): response = requests.request(method, f"{api}{path}", headers=headers, **kwargs) if response.ok: return None if response.status_code == 204 else response.json() error = response.json()["error"] raise MyClmError(response.status_code, error["code"], error["message"], error.get("details"))use GuzzleHttp\Exception\ClientException;
try { $http->get("$api/v1/signature-requests/$id", ['headers' => $headers]);} catch (ClientException $e) { $error = json_decode((string) $e->getResponse()->getBody(), true)['error']; if ($error['code'] === 'signature_request_not_found') { // … }}Error codes
Section titled “Error codes”Generated from the API source: every code below can be returned by the public API. Each operation of the API reference lists the codes it can return, with examples.
General
| Code | Status | Meaning |
|---|---|---|
invalid_request | 422 | The request does not match the schema. `details.issues` lists each problem (path, message). |
bad_request | 400 | The request body is not valid JSON (it cannot be parsed). |
payload_too_large | 413 | The request body exceeds the size limit. |
already_exists | 409 | A resource with the same unique key already exists. |
still_referenced | 409 | The resource is still referenced by another resource. |
concurrent_update | 409 | Concurrent update of the same resource: retry the request. |
precondition_failed | 412 | The resource changed since it was read (`If-Match` / `version` is stale): read it again, then retry. |
idempotency_key_reused | 422 | The `Idempotency-Key` was already used (within 24 hours) for a different request: use a new key. |
idempotency_in_progress | 409 | A request with the same `Idempotency-Key` is still running: retry a little later. |
idempotency_outcome_unknown | 409 | The request with the same `Idempotency-Key` was interrupted and its outcome is unknown (it is never run twice): check the resource, then retry with a new key. |
invalid_reference | 422 | A referenced resource does not exist. |
invalid_input | 422 | A value has an invalid format or is out of range. |
timeout | 503 | The request took too long and was canceled. Retry later. |
forbidden | 403 | The caller can see the resource but lacks the permission for this action. |
feature_disabled | 403 | The feature (signature, registered mail, tasks) is not enabled for this entity. |
quota_exceeded | 403 | The quota of the organization or entity is reached and set to block (see the `options` endpoints). |
not_found | 404 | Unknown route. |
entity_not_found | 404 | The entity does not exist or the caller has no access to it. |
invalid_page_token | 422 | The `pageToken` is malformed or does not belong to this list. |
invalid_contact | 422 | A linked `contactId` is not visible to the caller or does not belong to the resource entity. |
invalid_node_path | 422 | Internal node reference is invalid. |
rate_limited | 429 | Too many requests for this client in the current minute. Wait `Retry-After` seconds. |
internal_error | 500 | Unexpected error. Retry later; contact support with the time of the call if it persists. |
Authentication
| Code | Status | Meaning |
|---|---|---|
unauthenticated | 401 | Missing or malformed `Authorization: Bearer <token>` header. |
invalid_token | 401 | The token is invalid, expired, or was requested without the API audience scope. |
unknown_issuer | 401 | The token was issued by an identity instance that is not an active workspace. |
api_key_revoked | 401 | The API key was revoked. |
api_key_expired | 401 | The API key reached its expiry date. |
no_account | 403 | The token is valid but its subject has no account (or service account) in this workspace. |
ip_not_allowed | 403 | The API key has an IP allowlist and the request comes from another address. |
humans_only | 403 | Managing integrations (API keys, webhooks, grants) is reserved to people signed in to the app. |
unknown_client | 401 | The token was obtained by an OAuth client that is not a registered MyCLM client (the MyCLM apps, an API key, a partner application). |
Partner applications
| Code | Status | Meaning |
|---|---|---|
machine_credentials_required | 403 | Provisioning requires the machine credentials (`client_credentials`) of the partner application, not a delegated token. |
partner_app_revoked | 401 | The partner application was revoked. |
insufficient_scope | 403 | The partner application does not have the scope required by this operation. |
consent_required | 403 | The partner application has no access: no active grant on this organization (`details.orgId`), or, with a delegated token, the person has not authorized the application there. Send the person to `details.authorizeUrl` (delegated tokens). |
partner_app_only | 403 | Reserved to partner applications. |
role_escalation | 403 | The role cannot be given: partner applications cannot grant `owner`, and nobody can grant more rights than their own. |
external_id_required | 422 | An `externalId` is required to look up this resource. |
external_id_taken | 409 | This `externalId` is already used by another resource of the same kind. |
invalid_grant | 422 | The authorization code is invalid, expired, already used, or the `redirectUri` / `codeVerifier` does not match. |
grant_revoked | 409 | The grant behind the authorization code was revoked in the meantime. |
Signature
| Code | Status | Meaning |
|---|---|---|
signature_request_not_found | 404 | The signature request does not exist or the caller cannot see it. |
file_required | 422 | The multipart `file` part is missing. |
file_too_large | 422 | The PDF exceeds 15 MB. |
invalid_pdf | 422 | The file is not a readable PDF. |
signers_required | 422 | At least one signer is required. |
duplicate_signer_email | 422 | Two signers have the same email address. |
invalid_expiration | 422 | The expiry date is invalid. |
expiry_too_long | 422 | `expiresInDays` is above the maximum allowed by the entity settings. |
vendor_not_allowed | 422 | The vendor is not allowed by the entity settings (also used by registered mail). |
vendor_unavailable | 422 | The vendor is allowed but not configured on the platform. |
vendor_unsupported_level | 422 | The vendor cannot deliver the requested eIDAS level. |
eidas_level_not_allowed | 422 | The eIDAS level is not allowed by the entity settings. |
auth_method_not_allowed | 422 | The signer authentication method is not allowed (or weaker than the required one). |
signing_order_not_allowed | 422 | `signingOrder` can only be chosen when the entity lets the sender choose. |
external_signers_not_allowed | 422 | The entity only allows signers from the organization. |
handwritten_mention_requires_agency | 422 | A handwritten mention is drawn on site before sending: only for a signer who signs in agency (fields, participants and sending). |
handwritten_mention_drawing_required | 422 | A handwritten mention has no drawing yet (`details.fieldIds`, `details.participantIds`): have it drawn before sending. |
document_already_signed | 422 | The document already carries a digital signature: handwritten mentions cannot be burned into it (`details.documentId`). |
signature_field_not_found | 404 | No handwritten mention with this id in the signature request. |
notary_not_allowed | 422 | Notary participants are disabled by the entity settings. |
signer_phone_required | 422 | A phone number (E.164) is required for `sms_otp` authentication. |
signature_request_not_cancelable | 409 | Only requests waiting for signatures can be canceled. |
signature_request_completed | 409 | The request is already completed. |
signature_request_declined | 409 | The request was declined by a signer. |
signature_request_canceled | 409 | The request was canceled. |
signature_request_expired | 409 | The request expired. |
signature_request_not_completed | 409 | The signed document exists only once the request is completed. |
signed_document_not_available | 409 | The signed document is not available yet. |
document_purged | 409 | The document was deleted by the retention policy (metadata and proof are kept). |
document_archived | 409 | The document was archived by the retention policy. |
signature_vendor_missing | 409 | The vendor of this request is no longer configured. |
Registered mail
| Code | Status | Meaning |
|---|---|---|
registered_mail_not_found | 404 | The registered mail does not exist or the caller cannot see it. |
registered_mail_item_not_found | 404 | Unknown item for this registered mail. |
registered_mail_attachment_not_found | 404 | Unknown attachment for this registered mail. |
registered_mail_proof_not_found | 404 | This proof is not available (yet) for the item. |
recipient_update_not_found | 404 | Unknown recipient email change request. |
sender_account_not_found | 404 | The sender account does not exist or the caller cannot see it. |
subject_required | 422 | The subject is required. |
subject_too_long | 422 | The subject is too long for the vendor. |
recipients_required | 422 | At least one recipient is required to send. |
too_many_recipients | 422 | Too many recipients (max 50). |
too_many_followers | 422 | Too many followers (max 20). |
duplicate_recipient_email | 422 | Two recipients have the same email address. |
invalid_recipient | 422 | A recipient is incomplete. |
recipient_company_required | 422 | Professional recipients need a company name. |
sender_account_required | 422 | A verified sender account is required to send. |
invalid_sender_account | 422 | The sender account does not belong to this entity. |
sender_account_not_verified | 422 | The sender account is not verified at the vendor yet. |
sender_account_not_linked | 422 | The sender account is not linked to a vendor account. |
sender_account_vendor_mismatch | 422 | The sender account belongs to another vendor. |
sender_company_required | 422 | Professional sender accounts need a company name. |
sender_label_required | 422 | The sender account label is required. |
reference_required | 422 | The entity requires a `reference` on every registered mail. |
type_not_allowed | 422 | The registered mail type is not allowed by the entity settings. |
attachments_limit_exceeded | 422 | Too many attachments, or total size above the entity limit. |
registered_mail_already_sent | 409 | The mail was already sent with another idempotency key, or is no longer a draft. |
registered_mail_canceled | 409 | The registered mail was canceled. |
registered_mail_not_cancelable | 409 | Only drafts can be canceled. |
registered_mail_item_not_retryable | 409 | Only items that failed before the vendor accepted them can be retried. |
registered_mail_item_not_submitted | 409 | The item was not submitted to the vendor. |
recipient_update_already_answered | 409 | The recipient email change request was already answered. |
sender_account_exists | 409 | A sender account with this email already exists for the entity. |
vendor_error | 409 | The registered mail vendor refused the operation or is unreachable. `message` carries the vendor reason. |
Contacts
| Code | Status | Meaning |
|---|---|---|
contact_not_found | 404 | The contact does not exist or the caller cannot see it. |
contact_name_required | 422 | A person needs a first name or a last name. |
company_name_required | 422 | A company needs a name. |
company_link_person_only | 422 | Only persons can be linked to a company. |
invalid_company | 422 | `companyId` is not a visible company contact of the same entity. |
invalid_phone | 422 | `phone` is not a valid phone number. |
invalid_mobile_phone | 422 | `mobilePhone` is not a valid phone number. |
invalid_siren | 422 | Invalid SIREN (9 digits, Luhn checksum). |
invalid_siret | 422 | Invalid SIRET (14 digits, Luhn checksum). |
siren_siret_mismatch | 422 | The SIRET does not start with the SIREN. |
private_owner_only | 403 | Only the creator of a contact can change its visibility (`isPrivate`). |
merge_same_contact | 422 | A contact cannot be merged into itself. |
merge_kind_mismatch | 422 | Only contacts of the same kind can be merged. |
import_file_required | 422 | The multipart `file` part is missing. |
import_too_large | 422 | The CSV exceeds 5 MB or 5,000 rows. |
import_empty | 422 | The CSV has no data row. |
import_unknown_column | 422 | The mapping refers to a column that is not in the CSV header. |
Tasks
| Code | Status | Meaning |
|---|---|---|
task_not_found | 404 | The task does not exist or the caller cannot see it. |
invalid_assignee | 422 | The assignee cannot use tasks in this entity. |
invalid_occurrence | 422 | `occurrenceAt` is not an occurrence of the task. |
invalid_recurrence | 422 | The recurrence is invalid. |
invalid_timezone | 422 | Unknown IANA time zone. |
Structure & members
| Code | Status | Meaning |
|---|---|---|
organization_not_found | 404 | The organization does not exist or the caller has no access to it. |
node_not_found | 404 | The node does not exist or the caller cannot see it. |
membership_not_found | 404 | The membership does not exist or the caller cannot see it. |
role_not_found | 404 | The role does not exist in this organization. |
user_not_found | 404 | Unknown user. |
invalid_node_name | 422 | Invalid node name. |
invalid_parent | 422 | Entities go under the organization, groups under an entity or a group. |
max_depth_reached | 422 | The organization tree is too deep. |
role_other_org | 422 | The role belongs to another organization. |
membership_move_other_entity | 422 | A membership can only move inside its entity. |
cannot_delete_organization | 403 | The organization node itself cannot be deleted. |
node_not_empty | 409 | The node still has children or members. |
already_member | 409 | The user already has a membership in this entity. |
last_owner | 409 | The last owner of an organization cannot be removed or demoted. |
service_account | 409 | Service accounts (API keys) cannot be invited or provisioned as members. |