{
  "openapi": "3.1.0",
  "info": {
    "title": "Formify API",
    "description": "## Overview\n\nThe Formify API lets you send documents for electronic signature, prepare drafts, track signing status, and retrieve signed documents. It is designed for integrating signing workflows into your application.\n\n### Getting Access\n\nBefore you can use the API, you need an OAuth 2.0 client. Formify creates this for you based on information about your company, contacts, and redirect URIs. See the **[Getting Access](/guides/getting-access/)** guide for setup instructions.\n\nOnce your OAuth client has been created, you can use the [OAuth 2.0 Authorization Code flow](#tag/oauth) to obtain access tokens for API requests.\n\n### Authentication\n\nAll API requests require an OAuth 2.0 access token. To obtain one, your application redirects the user to Formify for authorization, receives an authorization code, and exchanges it for an access token. See the [OAuth 2.0 section](#tag/oauth) for the complete flow.\n\n### Environments\n\n**API base URL**: `https://docs-api.formify.eu/v1`  \nAll document operations, file uploads, and OAuth token exchange happen here.\n\n**Authorization Server**: `https://app.formify.eu`  \nThis is where users authorize your application and approve access (the OAuth consent).\n\n### Rate Limiting\n\nThe API uses token-based rate limiting to ensure fair usage:\n\n- **Headers**: Every response includes rate limit headers:\n  - `X-RateLimit-Limit`: Maximum requests allowed\n  - `X-RateLimit-Remaining`: Requests remaining in current window\n  - `X-RateLimit-Reset`: The number of seconds until the rate limit resets\n\nIf you exceed the limit, the API returns `429 Too Many Requests`. Wait until the reset time before making additional requests.\n\n---\n\n## Quick Start: Send a PDF for Signing\n\nOnce you have OAuth credentials and a valid `access_token`, you can send your first document for signing in three steps:\n\n### 1. Upload a PDF\nUpload a PDF file using the [Upload File](#tag/files/operation/uploadFile) endpoint:\n\n```bash\nPOST /files\nContent-Type: multipart/form-data\n```\n\nYou will receive a `fileId` in the response.\n\n### 2. Create a Signature Request\nSend the document for signing using the [Create Document](#tag/docs/operation/createDocument) endpoint:\n\n\n```bash\nPOST /docs\n{\n  \"fileId\": \"your-file-id\",\n  \"signeeDetails\": [\n    {\n      \"fullName\": \"John Doe\",\n      \"emailAddress\": \"john@example.com\",\n      \"signaturePlacement\": \"new_page\"\n    }\n  ]\n}\n```\n\nThe response returns a documentId. The signee then receives an email with a link to sign the document.\n\n### 3. Get Notified and Download\nRegister a [webhook](#tag/webhooks/operation/registerWebhook) to receive a notification when the document is signed:\n\n```bash\nPOST /webhooks\n{\n  \"webhookUrl\": \"https://your-app.com/webhook\",\n  \"eventTypes\": [\"document.completed\"]\n}\n```\n\nThen download the signed PDF using [Get Signed Document File](#tag/docs/operation/downloadSignedFile):\n\n```bash\nGET /docs/{documentId}/signed-file\n```\n\nFor more detailed walkthroughs, see the **[Integration Flows](/guides/integration-flows/)** guide.\n\n---\n\n### Help\n\n- **[Getting Access](/guides/getting-access/)** - How to set up OAuth 2.0 credentials\n- **[Integration Flows](/guides/integration-flows/)** - Step-by-step guides for common integration scenarios\n- **[Production Checklist](/guides/production-checklist/)** - Essential steps before going live\n\n### Links\n\n- [Formify API Changelog](/changelog) - Track API updates and changes\n- [Formify App](https://app.formify.eu) - Register OAuth applications and manage settings\n\n---",
    "contact": {
      "email": "api@formify.eu"
    },
    "version": "1.2.0",
    "termsOfService": "https://formify.eu/privacy/terms"
  },
  "servers": [
    {
      "url": "https://docs-api.formify.eu/v1",
      "description": "Main API server for all document related endpoints and OAuth 2.0 token exchange."
    }
  ],
  "security": [
    {
      "OAuth2": []
    }
  ],
  "tags": [
    {
      "name": "oauth",
      "description": "## OAuth 2.0 Authorization Code Grant Flow\n\nFormify uses OAuth 2.0 to allow third-party applications to access the API on behalf of users. This section provides a step-by-step guide to implementing the authorization flow.\n\n### Prerequisites\n\nSee the **[Getting Access](/guides/getting-access/)** guide for details, but in short you must:\n\n1. Obtain your `client_id` and `client_secret`.\n2. Register your application at [app.formify.eu](https://app.formify.eu)\n3. Configure your `redirect_uri` (must match exactly what you registered)\n\n### Authorization Flow\n\n#### Step 1: Redirect User to Authorization URL\n\nRedirect the user to Formify's authorization endpoint:\n\n```\nhttps://app.formify.eu/oauth?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=read%20write&state=RANDOM_STATE_STRING\n```\n\n**Parameters:**\n- `client_id` (required): Your application's client ID\n- `redirect_uri` (required): Where to redirect after authorization (must be registered)\n- `response_type` (required): Always use `code` for authorization code flow\n- `scope` (required): Space-separated list of permissions (`read`, `write`, or `read write`)\n- `state` (recommended): Random string to prevent CSRF attacks\n\n#### Step 2: User Authorizes Your Application\n\nThe user sees a consent screen and approves your application's access to their Formify account.\n\n#### Step 3: Receive Authorization Code\n\nAfter approval, Formify redirects back to your `redirect_uri` with an authorization code:\n\n```\nhttps://your-app.com/callback?code=AUTH_CODE_HERE&state=RANDOM_STATE_STRING\n```\n\n**Important:** Verify that the `state` parameter matches what you sent in Step 1.\n\n#### Step 4: Exchange Code for Access Token\n\nMake a POST request to the token endpoint to exchange the authorization code for an access token:\n\n```bash\nPOST https://docs-api.formify.eu/v1/oauth/token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=authorization_code\n&code=AUTH_CODE_HERE\n&redirect_uri=YOUR_REDIRECT_URI\n&client_id=YOUR_CLIENT_ID\n&client_secret=YOUR_CLIENT_SECRET\n```\n\n**Response:**\n```json\n{\n  \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"refresh_token\": \"def502004a1b2c3d...\",\n  \"scope\": \"read write\"\n}\n```\n\n#### Step 5: Use Access Token\n\nInclude the access token in the `Authorization` header for all API requests:\n\n```bash\nGET https://docs-api.formify.eu/v1/docs\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n```\n\n### Token Refresh\n\nAccess tokens expire after the time specified in `expires_in` (typically 1 hour). Use the refresh token to obtain a new access token without requiring user interaction:\n\n```bash\nPOST https://docs-api.formify.eu/v1/oauth/token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=refresh_token\n&refresh_token=YOUR_REFRESH_TOKEN\n&client_id=YOUR_CLIENT_ID\n&client_secret=YOUR_CLIENT_SECRET\n```\n\n**Response:** Same structure as the initial token response, including a new `refresh_token`.\n\n### Scope\n\n- **`read`**: Allows reading documents, files, user info, and account settings\n- **`write`**: Allows creating/updating documents, uploading files, and modifying settings\n\nRequest only the scopes your application needs. Users see which permissions you're requesting during authorization.\n\n### Common Errors\n\n| Error | Description | Solution |\n|-------|-------------|----------|\n| `invalid_grant` | Authorization code expired or already used | Codes expire after 10 minutes. Start the flow again. |\n| `invalid_client` | Invalid client_id or client_secret | Verify your credentials in app.formify.eu |\n| `redirect_uri_mismatch` | redirect_uri doesn't match registered URI | Must match exactly (including trailing slash) |\n| `invalid_scope` | Requested scope not allowed | Use only `read` and/or `write` |\n| `access_denied` | User denied authorization | User must approve for your app to work |\n\n### Security Best Practices\n\n- **Never expose** `client_secret` in client-side code\n- **Always use HTTPS** for redirect URIs\n- **Validate state parameter** to prevent CSRF attacks\n- **Store tokens securely** (encrypted database, secure session storage)\n- **Refresh tokens proactively** before they expire to avoid service interruption"
    },
    {
      "name": "files",
      "description": "Upload files to use for signature requests"
    },
    {
      "name": "docs",
      "description": "Handle and create document signature requests"
    },
    {
      "name": "user",
      "description": "User info and operations"
    },
    {
      "name": "account",
      "description": "Account info and operations"
    },
    {
      "name": "webhooks",
      "description": "Webhooks operations"
    },
    {
      "name": "drafts",
      "description": "Drafts allow you to save a document as a work-in-progress before sending it for signing.\nA draft stores all document settings (name, signees, signature placements, AI assistant, etc.)\nwithout actually sending invitations to signees.\n\n**Draft Lifecycle:**\n1. **Create** a draft with `POST /docs/drafts` — saves the document configuration\n2. **Update** the draft with `PUT /docs/drafts/{draftId}` — modify any settings before sending\n3. Optionally **preview** the draft with `GET /docs/drafts/{draftId}/file` — returns a PDF showing signature field placements\n4. **Send** the draft with `POST /docs/drafts/{draftId}/send` — converts the draft into a live document and sends invitations to signees\n5. Optionally **list** drafts with `GET /docs/drafts` or **get details** with `GET /docs/drafts/{draftId}`\n6. **Delete** a draft with `DELETE /docs/drafts/{draftId}` if it's no longer needed\n\n**Key Differences from `POST /docs`:**\n- Drafts do **not** send invitations to signees upon creation — they are saved for later.\n- Signee details are optional when creating/updating a draft. You can add them later via `PUT /docs/drafts/{draftId}`.\n- Signees in a draft do not need a contact method (email/phone) until the draft is sent.\n- Signature placement coordinates can be omitted or incomplete during the draft phase.\n- When sent via `POST /docs/drafts/{draftId}/send`, all the same validations as `POST /docs` apply (contact methods, signature coordinates, etc.).\n- Drafts expire automatically after the same auto-deletion period as other documents. Expired drafts cannot be edited or sent.\n\n**Shared Properties with `POST /docs`:**\nThe request body for creating and updating drafts shares many of the same properties as `POST /docs`, including:\n- `signeeDetails` (signature types, placements, signatureBox, idScanBox, signingOrder)\n- `enableSigningOrder` (requires `signingOrder` capability)\n- `aiAssistant` configuration\n- `language`, `sharingSetting`, `personalMessage`\n- Signature type permission requirements (`signatureBankId`, `signatureIdScan`, `signatureFaceLiveness` capabilities)\n\nRefer to the `POST /docs` documentation above for details on these shared properties."
    },
    {
      "name": "templates",
      "description": "Template operations"
    }
  ],
  "externalDocs": {
    "description": "Formify website",
    "url": "https://formify.eu"
  },
  "paths": {
    "/oauth": {
      "get": {
        "tags": [
          "oauth"
        ],
        "summary": "OAuth authorize URL (browser redirect, not an API request)",
        "description": "> **This is not a REST endpoint.** Redirect the user's browser to this URL — do not call it as a server-to-server API request.\n\n### Step 1: Requesting authorization\n\nFirst, redirect the customer's web browser to the authorize endpoint in the Formify app. The customer will be asked to authorize your app to access their Formify account.\n\n**Authorize URL:**\n```\nGET https://app.formify.eu/oauth?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code&scope=read%20write&state={state}\n```\n\n**Parameters:**\n\n| Parameter       | Required | Description                                                                                           |\n|-----------------|----------|-------------------------------------------------------------------------------------------------------|\n| `client_id`     | Yes      | The client ID provided to you by Formify when you register your app.                                  |\n| `redirect_uri`  | Yes      | The callback URL you provided when you registered your app. This should be URL-encoded.               |\n| `response_type` | Yes      | Must be `code`.                                                                                       |\n| `scope`         | Yes      | Requested access scope. One of `read`, `write`, or `read write` (space-separated).                   |\n| `state`         | Recommended | Strongly recommended for CSRF protection. Send a random value and verify that the same value is returned to your callback.  |\n\n**Example of URL-encoded redirect_uri:**\nhttps%3A%2F%2Fwww.your-domain.com%2Foauth%2Fcallback\n\n### Step 2: Customer Authorizes the App\n\nThe customer will see a confirmation dialog. If they authorize, they will be redirected to the callback URL with a code parameter:\n\n```\nGET https://your-callback-url.com?code={authorization_code}&state={state}\n```\n\n### Step 3: Exchange Authorization Code for Access Token\n\nMake a `POST` request to `/oauth/token` through the API to exchange the authorization code for an access token and refresh token. The refresh token is normally valid for 60 days.\n\n**Request:**\n\n```\nPOST https://docs-api.formify.eu/v1/oauth/token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=authorization_code&code={authorization_code}&client_id={client_id}&client_secret={client_secret}&redirect_uri={redirect_uri}\n```\n\n**Response:**\n\n```\n{\n  \"access_token\": \"{access_token}\",\n  \"expires_in\": 3600,\n  \"refresh_token\": \"{refresh_token}\",\n  \"token_type\": \"Bearer\",\n  \"scope\": \"read write\"\n}\n```\n",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Browser redirect",
            "source": "const params = new URLSearchParams({\n  client_id: \"YOUR_CLIENT_ID\",\n  redirect_uri: \"https://your-app.com/callback\",\n  response_type: \"code\",\n  scope: \"read write\",\n  state: \"RANDOM_STATE_STRING\"\n});\n\nwindow.location.href = `https://app.formify.eu/oauth?${params.toString()}`;"
          },
          {
            "lang": "Python",
            "label": "Backend redirect (Flask)",
            "source": "from urllib.parse import urlencode\nfrom flask import redirect\n\nparams = urlencode({\n    \"client_id\": \"YOUR_CLIENT_ID\",\n    \"redirect_uri\": \"https://your-app.com/callback\",\n    \"response_type\": \"code\",\n    \"scope\": \"read write\",\n    \"state\": \"RANDOM_STATE_STRING\",\n})\n\nreturn redirect(f\"https://app.formify.eu/oauth?{params}\")"
          },
          {
            "lang": "curl",
            "label": "Authorize URL",
            "source": "# Open this URL in the user's browser — do not call it from your server\nhttps://app.formify.eu/oauth?client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fyour-app.com%2Fcallback&response_type=code&scope=read%20write&state=RANDOM_STATE_STRING"
          }
        ],
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The client ID for your application. Find it in the Formify app under **Settings → Integration → My applications → Details**."
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Strongly recommended for CSRF protection. Send a random value and verify that the same value is returned to your callback."
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The callback (redirect) URL registered for your app. Manage your callback URLs in the Formify app under **Settings → Integration → My applications → Details**."
          },
          {
            "name": "response_type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "code"
              ]
            },
            "description": "Must be `code`."
          },
          {
            "name": "scope",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "read",
                "write",
                "read write"
              ]
            },
            "description": "Requested access scope. Use `read write` (space-separated) for full access."
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "302": {
            "description": "Redirection to the provided `redirect_uri` with the authorization code as a query parameter."
          }
        },
        "servers": [
          {
            "url": "https://app.formify.eu",
            "description": "Authorization server for handling OAuth 2.0 authorization flows."
          }
        ],
        "security": []
      }
    },
    "/oauth/token": {
      "post": {
        "tags": [
          "oauth"
        ],
        "summary": "Generate OAuth 2.0 access token using authorization code or refresh token",
        "description": "This endpoint is used to exchange an authorization code or refresh token for an access token. \n\n### Notes:\n- The **Authorization Code Grant Flow** requires a `grant_type` of `authorization_code` and a valid `code` obtained from the authorization URL.\n- The **Refresh Token Grant Flow** requires a `grant_type` of `refresh_token` and a valid `refresh_token`.\n- Traditional clients authenticate using **HTTP Basic Authentication**: send `Authorization: Basic base64(client_id:client_secret)`.\n- Public clients (URL-based `client_id`) do not send credentials in the header — instead, send `client_id` in the request body.\n\n### Public clients (Client ID Metadata Document)\n\nIf your `client_id` is an HTTPS URL pointing to a metadata document, do not send a `client_secret`. Instead, include a `code_verifier` (PKCE) when exchanging an authorization code. See the [OAuth for AI & Third-Party Clients](/guides/oauth-for-ai/) guide.",
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/oauth/token\" \\\n  -u \"YOUR_CLIENT_ID:YOUR_CLIENT_SECRET\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"grant_type=authorization_code\" \\\n  -d \"code=AUTH_CODE_HERE\" \\\n  -d \"redirect_uri=https://your-app.com/callback\""
          },
          {
            "lang": "JavaScript",
            "source": "const credentials = btoa(\"YOUR_CLIENT_ID:YOUR_CLIENT_SECRET\");\n\nconst body = new URLSearchParams({\n  grant_type: \"authorization_code\",\n  code: \"AUTH_CODE_HERE\",\n  redirect_uri: \"https://your-app.com/callback\"\n});\n\nconst response = await fetch(\"https://docs-api.formify.eu/v1/oauth/token\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/x-www-form-urlencoded\",\n    \"Authorization\": `Basic ${credentials}`\n  },\n  body\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/oauth/token\"\ndata = {\n    \"grant_type\": \"authorization_code\",\n    \"code\": \"AUTH_CODE_HERE\",\n    \"redirect_uri\": \"https://your-app.com/callback\",\n}\n\nresponse = requests.post(url, data=data, auth=(\"YOUR_CLIENT_ID\", \"YOUR_CLIENT_SECRET\"))\nprint(response.json())"
          }
        ],
        "operationId": "generateOauthAccessToken",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "authorization_code",
                      "refresh_token"
                    ],
                    "description": "The type of grant being used."
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Only for public clients (URL-based): the HTTPS metadata document URL. Traditional clients must not send credentials in the body — use `Authorization: Basic base64(client_id:client_secret)` instead."
                  },
                  "code": {
                    "type": "string",
                    "description": "The authorization code issued to the client. Required when grant_type=authorization_code."
                  },
                  "redirect_uri": {
                    "type": "string",
                    "description": "The redirect URI used in the authorization request. Required when grant_type=authorization_code."
                  },
                  "refresh_token": {
                    "type": "string",
                    "description": "The refresh token issued to the client. Required when grant_type=refresh_token."
                  },
                  "code_verifier": {
                    "type": "string",
                    "description": "PKCE code verifier. Required for URL-based public clients when grant_type=authorization_code."
                  },
                  "scope": {
                    "type": "string",
                    "description": "Space-separated list of scopes. Supported values: `read`, `write`. Use `read write` for full access. Defaults to `read write` if omitted.",
                    "example": "read write"
                  },
                  "error_language": {
                    "type": "string",
                    "enum": [
                      "en",
                      "sv",
                      "es"
                    ],
                    "description": "Language for the error message. Note: this field uses snake_case (`error_language`) following OAuth 2.0 form-encoded conventions, unlike `errorLanguage` used on other endpoints."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string",
                      "description": "The access token."
                    },
                    "expires_in": {
                      "type": "integer",
                      "description": "The number of seconds until the access token expires."
                    },
                    "refresh_token": {
                      "type": "string",
                      "description": "The refresh token."
                    },
                    "token_type": {
                      "type": "string",
                      "description": "The type of token."
                    },
                    "scope": {
                      "type": "string",
                      "description": "Space-separated list of permissions granted to this token (e.g. `read`, `write`, or `read write`).",
                      "example": "read write"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid client credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "basicAuth": []
          },
          {}
        ]
      }
    },
    "/oauth/revoke": {
      "post": {
        "tags": [
          "oauth"
        ],
        "summary": "Revoke OAuth 2.0 token and its associated grant",
        "description": "This endpoint is used to revoke an OAuth 2.0 token, which invalidates its associated grant. \n\n### Notes:\n- You can revoke either using an `access_token` or a `refresh_token`.\n- **Public clients** (URL-based `client_id`) must include their `client_id` in the request body and can only revoke using `token_type_hint=refresh_token`.\n\n### Example Usage:\n#### Request Body:\n```json\n{\n  \"token\": \"your_refresh_token_here\",\n  \"token_type_hint\": \"refresh_token\"\n}\n```\n\n#### Response:\nIf the token is successfully revoked:\n- HTTP Status: `200 OK` (no body content required).\n\nIf the token is invalid or has already been revoked:\n- HTTP Status: `400 Bad Request`.",
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/oauth/revoke\" \\\n  -u \"YOUR_CLIENT_ID:YOUR_CLIENT_SECRET\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"token=your_refresh_token_here\" \\\n  -d \"token_type_hint=refresh_token\""
          },
          {
            "lang": "JavaScript",
            "source": "const credentials = btoa(\"YOUR_CLIENT_ID:YOUR_CLIENT_SECRET\");\n\nconst response = await fetch(\"https://docs-api.formify.eu/v1/oauth/revoke\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/x-www-form-urlencoded\",\n    \"Authorization\": `Basic ${credentials}`\n  },\n  body: new URLSearchParams({\n    token: \"your_refresh_token_here\",\n    token_type_hint: \"refresh_token\"\n  })\n});\n\nconsole.log(response.status); // 200"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/oauth/revoke\"\ndata = {\n    \"token\": \"your_refresh_token_here\",\n    \"token_type_hint\": \"refresh_token\",\n}\n\nresponse = requests.post(url, data=data, auth=(\"YOUR_CLIENT_ID\", \"YOUR_CLIENT_SECRET\"))\nprint(response.status_code)  # 200"
          }
        ],
        "operationId": "revokeToken",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "The token to be revoked (either `access_token` or `refresh_token`)."
                  },
                  "token_type_hint": {
                    "type": "string",
                    "enum": [
                      "access_token",
                      "refresh_token"
                    ],
                    "description": "Hint about the type of token being revoked."
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Required for public clients. Not needed for confidential clients using HTTP Basic authentication."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token successfully revoked."
          },
          "400": {
            "description": "Invalid or already revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid client credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "basicAuth": []
          },
          {}
        ]
      }
    },
    "/oauth/register": {
      "post": {
        "tags": [
          "oauth"
        ],
        "summary": "Dynamic Client Registration (RFC 7591)",
        "description": "Register an OAuth 2.0 client programmatically. Returns a `client_id` (GUID) and, for confidential clients, a `client_secret`.\n\n### Public vs. confidential clients\n- **Public clients** (`token_endpoint_auth_method: \"none\"`): Must use PKCE (S256) for all authorization requests. No `client_secret` is issued.\n- **Confidential clients** (`token_endpoint_auth_method: \"client_secret_basic\"`): Receive a `client_secret` for HTTP Basic authentication. The secret is only returned once at registration time.\n\n### Rate limiting\nThis endpoint is rate limited. Exceeding the limit returns `429 Too Many Requests`.\n\nSee the [OAuth for AI & Third-Party Clients](/guides/oauth-for-ai/#option-b-dynamic-client-registration) guide for full details.",
        "operationId": "registerClient",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_name",
                  "redirect_uris"
                ],
                "properties": {
                  "client_name": {
                    "type": "string",
                    "maxLength": 512,
                    "description": "Human-readable name shown to users during authorization."
                  },
                  "redirect_uris": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 10,
                    "description": "Array of allowed callback URLs. Must be absolute HTTPS URLs without fragments."
                  },
                  "token_endpoint_auth_method": {
                    "type": "string",
                    "enum": [
                      "none",
                      "client_secret_basic"
                    ],
                    "default": "none",
                    "description": "`\"none\"` for public clients, `\"client_secret_basic\"` for confidential clients."
                  },
                  "grant_types": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "authorization_code",
                        "refresh_token"
                      ]
                    },
                    "default": [
                      "authorization_code"
                    ],
                    "description": "Informational. Array of grant types the client intends to use."
                  },
                  "response_types": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "code"
                      ]
                    },
                    "default": [
                      "code"
                    ],
                    "description": "Array of response types. Only `\"code\"` is supported."
                  },
                  "client_uri": {
                    "type": "string",
                    "maxLength": 2048,
                    "description": "URL to the application's homepage."
                  },
                  "logo_uri": {
                    "type": "string",
                    "maxLength": 2048,
                    "description": "URL to the application's logo, displayed during authorization consent."
                  },
                  "tos_uri": {
                    "type": "string",
                    "maxLength": 2048,
                    "description": "Link to Terms of Service."
                  },
                  "policy_uri": {
                    "type": "string",
                    "maxLength": 2048,
                    "description": "Link to Privacy Policy."
                  },
                  "scope": {
                    "type": "string",
                    "maxLength": 1024,
                    "description": "Space-separated list of scopes the client intends to request."
                  },
                  "contacts": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 5,
                    "description": "Array of contact email addresses."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Client registered successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client_id": {
                      "type": "string",
                      "description": "The registered client identifier (GUID)."
                    },
                    "client_name": {
                      "type": "string",
                      "description": "The registered client name."
                    },
                    "redirect_uris": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The registered redirect URIs."
                    },
                    "grant_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The registered grant types."
                    },
                    "response_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The registered response types."
                    },
                    "token_endpoint_auth_method": {
                      "type": "string",
                      "description": "`\"none\"` or `\"client_secret_basic\"`."
                    },
                    "client_id_issued_at": {
                      "type": "integer",
                      "description": "Unix timestamp when the client was registered."
                    },
                    "client_secret": {
                      "type": "string",
                      "description": "Only for confidential clients. The client secret."
                    },
                    "client_secret_expires_at": {
                      "type": "integer",
                      "description": "Only for confidential clients. `0` means no expiration."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {}
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/oauth/register\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"client_name\": \"string\",\n  \"redirect_uris\": [\n    \"string\"\n  ]\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/oauth/register\", {\n  method: \"POST\",\n  headers: {    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"client_name\": \"string\",\n    \"redirect_uris\": [\n        \"string\"\n    ]\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/oauth/register\"\nheaders = {}\npayload = {\n    \"client_name\": \"string\",\n    \"redirect_uris\": [\n        \"string\"\n    ]\n}\n\nresponse = requests.post(url, json=payload)\nprint(response.json())"
          }
        ]
      }
    },
    "/files": {
      "post": {
        "tags": [
          "files"
        ],
        "summary": "Upload a PDF file for signing",
        "description": "Uploads a PDF file to Formify, making it available for creating document signature requests via the `POST /docs` endpoint.\n\n### Requirements\n- **File Format**: Only PDF files are supported.\n- **File Size**: Maximum allowed file size is **50 MB**.\n- **Unlocked PDFs Only**: The file must not be password-protected or locked in any way. Locked files cannot be processed.\n- **No Pre-Signed Documents**: The PDF must not contain digital signatures from other services, as these will be removed, invalidating the document's signature integrity.\n- **Signature Field Handling**: If the uploaded document contains existing signature fields (interactive PDF form fields of type `signature`), these will be removed automatically. New signature fields, fully compatible with the Formify signing process, must be added to the document instead. This ensures that all signature fields function seamlessly within the Formify platform.\nBy ensuring these requirements are met, you can use the uploaded file to create a document signature request seamlessly.",
        "operationId": "uploadFile",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The PDF document to be uploaded. Max file size: 50 MB."
                  },
                  "errorLanguage": {
                    "type": "string",
                    "enum": [
                      "en",
                      "sv",
                      "es"
                    ],
                    "description": "Language for the error message"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "File uploaded successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fileId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The unique identifier of the uploaded file"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters or issues with the file",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "File exceeds the maximum allowed size of 50 MB."
                    },
                    "code": {
                      "type": "integer",
                      "example": 400
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Access to the file is denied",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Unauthorized access to the file."
                    },
                    "code": {
                      "type": "integer",
                      "example": 403
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/files\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: multipart/form-data\" \\\n  -F \"file=@file.pdf\" \\\n  -F \"errorLanguage=en\""
          },
          {
            "lang": "JavaScript",
            "source": "const formData = new FormData();\nformData.append(\"file\", fileBlob, \"file.pdf\");\nformData.append(\"errorLanguage\", \"en\");\n\nconst response = await fetch(\"https://docs-api.formify.eu/v1/files\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  },\n  body: formData\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/files\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\nfiles = {\n    \"file\": open(\"file.pdf\", \"rb\"),\n    \"errorLanguage\": (None, \"en\")\n}\n\nresponse = requests.post(url, headers=headers, files=files)\nprint(response.json())"
          }
        ]
      }
    },
    "/files/merge": {
      "post": {
        "tags": [
          "files"
        ],
        "summary": "Merge multiple PDF files into a single document",
        "description": "Merges two or more uploaded PDF files into a single file. The resulting file will be assigned a new `fileId`.\n\n### Requirements\n- **File Format**: Only pre-uploaded PDF files are supported for merging.\n- **Order of Merge**: The files will be merged in the order of the provided `fileIds`.\n- **File Name**: A name for the resulting merged file must be provided.\n- **Max File Size**: Ensure the combined file size does not exceed system limits (**50 MB**).\n- **2-8 Files**: A minimum of 2 files and a maximum of 8 `fileIds` are required (duplicate `fileIds` are not allowed.).\n\nThis endpoint is particularly useful for workflows where multiple PDF files need to be combined before sending them for signatures or further processing.",
        "operationId": "mergeFiles",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fileIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Unique identifier of a file to be merged."
                    },
                    "description": "List of File IDs to merge. Must contain between 2 and 8 unique File IDs.",
                    "example": [
                      "123e4567-e89b-12d3-a456-426614174000",
                      "123e4567-e89b-12d3-a456-426614174001"
                    ]
                  },
                  "name": {
                    "type": "string",
                    "description": "Name for the resulting merged file.",
                    "example": "Merged Document"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Files merged successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fileId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The unique identifier of the merged file.",
                      "example": "123e4567-e89b-12d3-a456-426614174002"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters or issues with the files.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Invalid request: Must provide between 2 and 8 unique File IDs."
                    },
                    "code": {
                      "type": "integer",
                      "example": 400
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Access to one or more files is denied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Unauthorized access to one or more files."
                    },
                    "code": {
                      "type": "integer",
                      "example": 403
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/files/merge\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"fileIds\": [\n    \"123e4567-e89b-12d3-a456-426614174000\",\n    \"123e4567-e89b-12d3-a456-426614174001\"\n  ],\n  \"name\": \"Merged Document\"\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/files/merge\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"fileIds\": [\n        \"123e4567-e89b-12d3-a456-426614174000\",\n        \"123e4567-e89b-12d3-a456-426614174001\"\n    ],\n    \"name\": \"Merged Document\"\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/files/merge\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = {\n    \"fileIds\": [\n        \"123e4567-e89b-12d3-a456-426614174000\",\n        \"123e4567-e89b-12d3-a456-426614174001\"\n    ],\n    \"name\": \"Merged Document\"\n}\n\nresponse = requests.post(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      }
    },
    "/files/{fileId}": {
      "delete": {
        "tags": [
          "files"
        ],
        "summary": "Delete an uploaded file",
        "operationId": "deleteFileById",
        "parameters": [
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the file to delete"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "204": {
            "description": "File successfully deleted"
          },
          "400": {
            "description": "Invalid request parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Not Found - File not found or already deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X DELETE \"https://docs-api.formify.eu/v1/files/{fileId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/files/{fileId}\", {\n  method: \"DELETE\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconsole.log(response.status); // 204"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/files/{fileId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.delete(url, headers=headers)\nprint(response.status_code)  # 204"
          }
        ]
      }
    },
    "/files/{fileId}/fields": {
      "get": {
        "tags": [
          "files"
        ],
        "summary": "Retrieve all form fields for an uploaded file",
        "description": "Retrieves all the form fields defined in a specific file before it is used to create a document. This is useful for pre-filling values or understanding the structure of the form.\n\n### Notes:\n- Fields retrieved here may include metadata such as `readOnly` (not possible to modify by the signee) and `editable` to indicate whether they can be modified or pre-filled.\n- For getting actual values in a signed document, use the `/docs/{documentId}/field-values` endpoint.",
        "operationId": "getFileFields",
        "parameters": [
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "description": "The unique identifier of the uploaded file.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved form fields.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FormField"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "File not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/files/{fileId}/fields\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/files/{fileId}/fields\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/files/{fileId}/fields\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs": {
      "post": {
        "tags": [
          "docs"
        ],
        "summary": "Create a document and request signatures",
        "description": "This endpoint allows you to create a document and request signatures. You can:\n- Provide a `fileId` to create a document from an uploaded file.\n- Provide a `templateId` to create a document using a pre-configured template.\n\n### Requirements\n- **fileId**: Use this to create a document from an uploaded file. Use `POST /files` to upload a file beforehand.\n- **templateId**: Use this to create a document from an existing template. Use `POST /templates` to create a template, and `GET /templates` to list templates.\n- **Either `fileId` or `templateId` must be provided**, but not both. If both are included, the request will fail with a `400 Bad Request` error.\n- **Template Feature Activation**:  \n  Requires the `templates` capability. Check `GET /account/capabilities`.\n\n### Ownership\nThe **User ID** (`userId`) parameter can be optionally specified to assign the document ownership to a different user within the same account. If omitted, the document will be owned by the authenticated user. This user will receive document status updates by email.\n\n### Signee Signature Type\nThe **Signature Type** defines the method by which the signee will provide their signature. Specify the desired method using the `signatureType` parameter:\n1. **Digital Ink** (`digital_ink`):  \n  The signee can draw their signature using a mouse, touchpad, or finger. This signature type complies with **Simple Electronic Signature (SES)** standards.\n2. **BankID Identification** (`bankid_identification`):\n  The signee will securely sign using Swedish **BankID** for identification. This signature type complies with **Advanced Electronic Signature (AES)** standards.\n  > **Note:** To achieve AES compliance for a document, all signatures within the document must be of the AES level.\n  > **Availability:** Requires the `signatureBankId` capability. Check `GET /account/capabilities`.\n\n3. **ID Scan + Ink Signature** (`digital_ink_id_scan`):\n  The signee verifies identity by scanning an ID document and then signs using digital ink.\n  > **Placement requirement:** This signature type only supports `existing` placement and requires both `signatureBox` and `idScanBox`.\n  > **Availability:** Requires the `signatureIdScan` capability. Check `GET /account/capabilities`.\n\n4. **Face Liveness** (`face_liveness`):\n  The signee verifies identity using an ID scan together with a video selfie biometric liveness check.\n  > **Availability:** Requires the `signatureFaceLiveness` capability. Check `GET /account/capabilities`.\n\n### Signature Field Placement\nYou can choose one of two methods for placing signature fields on the document:\n\n1. **Automatic Placement** (`new_page`): The signature field will appear on a new page at the document's end (in A4 portrait format).\n   - Not supported for `digital_ink_id_scan`.\n2. **Custom Placement with Coordinates (`existing`)**:\n   - Use the `signatureBox` property to specify precise `x` and `y` coordinates, the `page` number, and an optional `scale` value.\n   - Measurements are in points, and tools like Adobe Illustrator can help determine the exact position on the document.\n   - Validation Rules:\n     - `page` must be >= 0 and within the bounds of the document's page count.\n     - `x` and `y` must be >= 0.\n     - `scale` defaults to `1.0`, with a minimum of `0.25` and a maximum of `1.5`.\n   - If `signatureBox` is not provided in the request:\n     - Values will fall back to the template's `x`, `y`, `page`, `scale`, and `formFieldId`, if available.\n\nEach signature field is standardized in size before scaling:\n- **Width:** 219 points\n- **Height:** 58 points\n\n### Additional Placement for ID Scan + Ink Signature\nWhen using `digital_ink_id_scan`, you must also provide an `idScanBox` inside the document.\n\n- `idScanBox` defines the position of the scanned ID placeholder.\n- `signaturePlacement` must be `existing` for this signature type.\n- Both `signatureBox` and `idScanBox` are required.\n- `idScanBox.scale` defaults to `1.0`, with a minimum of `0.25` and a maximum of `1.5`.\n\nThe ID scan placeholder has the following base size before scaling:\n- **Width:** 218 points\n- **Height:** 138 points\n\n### Signee Contact Methods\nEach signee must have at least one contact method specified to receive the document:\n- **Email Address** (`emailAddress`) or **Phone Number** (`phoneNumber`): Required for distributing the document. If using SMS notifications, additional transaction fees apply.\n- **Phone Numbers** must include the country code.\n- **Phone Text Message Delivery Method** (`phoneNumberDeliveryMethod`) is possible to set to either:  \n  - **sms**: Sends a regular text message to the provided phone number (default, requires `deliverySms` capability)\n  - **WhatsApp**: Sends a WhatsApp text message to the provided phone number (requires `deliveryWhatsapp` capability)\n\nBy specifying the appropriate placement method and contact information, you ensure seamless delivery and positioning of signature fields in the document.\n\n### Signing Order\nThe **Signing Order** feature allows you to control the sequence in which signees receive invitations and can sign the document. This is useful when signatures must be collected in a specific order.\n- **Enabling Signing Order**: Set `enableSigningOrder` to `true` in the request to activate this feature.\n- **Setting Order Values**: Use the `signingOrder` property in each signee's details within the `signeeDetails` array to specify their position in the sequence.\n  - **Sequential Signing**: Assign different order values (1, 2, 3, etc.) to enforce a strict signing sequence.\n  - **Simultaneous Signing**: Assign the same order value to multiple signees to allow them to sign at the same time.\n  - **Default Behavior**: If `enableSigningOrder` is `false` (default), all signees are treated as having order 1 and can sign simultaneously.\n- **Invitation Delivery**: Signees will only receive their invitation (email/SMS/WhatsApp) when it is their turn to sign based on their `signingOrder` value.\n- **Signing Order Feature Activation**:  \n  Requires the `signingOrder` capability. Check `GET /account/capabilities`.\n\n### Pre-filling Form Fields\nUse the optional `fields` property to pre-fill values in form fields:\n- **For `fileId`**: Use `GET /files/{fileId}/fields` to retrieve the list of fields in the uploaded file.\n- **For `templateId`**: Use `GET /templates/{templateId}/fields` to retrieve template-specific fields.\n\nOnly editable fields (`editable` from `GET /files/{fileId}/fields` or `GET /templates/{templateId}/fields`) can be pre-filled through the API.\n\n### When Using a Template\n- If you provide a `templateId`, the document will be created based on the specified template. This way, the template can place signature fields (and define the document's other fields/settings) in advance.\n- For each signee in the `signeeDetails` array:\n  - **Full Name (`fullName`), Email Address (`emailAddress`), and Phone Number (`phoneNumber`)**:\n    These should be provided in the request and will not fall back to the template. You must specify at least one contact method (email or phone) for each signee.\n  - **Signature Type**:  \n    Defaults to the template's value if not specified in the request.\n  - **Signature Placement (`signaturePlacement`)**:  \n    Defaults to the template's value if not specified in the request.\n  - **Signature Box (`signatureBox`)**:\n    The following properties will use the template defaults if not provided in the request:\n    - `x`, `y`, `page`, `scale`\n    - These are required for `existing` signature placement. If missing in both the request and template, the request will fail.\n  - **ID Scan Box (`idScanBox`)**:\n    Only applicable when `signatureType` is `digital_ink_id_scan`.\n    - `x`, `y`, `page`, `scale`\n    - These are required for `digital_ink_id_scan`. If missing in both the request and template, the request will fail.\n\n### Form Fields Read-Only Mode (`fieldsReadonlyMode`)\n  Determines how form fields should be handled in terms of read-only restrictions:\n\n  1. **`keepOriginal`** (Default) – Retains the original field settings without modifying readonly state.\n  2. **`filled`** – Set **pre-filled** fields readonly, leaving empty fields editable.\n  3. **`all`** – Locks **all** fields, setting them readonly to prevent modifications.\n\n### AI Assistant (`aiAssistant`)\n  Configure an optional AI Assistant that helps signees while reviewing and signing the document.\n  \n  - **`enabled`**: Set to `true` to activate the assistant for this document (default: `false`)\n  - **`textToSpeech`**: Enable text-to-speech so the assistant can read responses aloud (default: `false`)\n  - **`language`**: Set the assistant's language (default: `en`)\n  \n  The AI Assistant can answer questions about the document content and guide signees through the signing process. It supports 28 languages including English, Swedish, Spanish, German, French, and more.\n\n  **Important:** When the AI Assistant is enabled, the document creation is asynchronous:\n  - The response will have `status: \"processing\"` instead of `status: \"created\"`\n  - The `documentUrl` field will be `null` until processing is complete\n  - Signee invitations (email/SMS/WhatsApp) will be sent automatically once processing is complete\n  - Use `GET /docs/{documentId}` to check the document status\n  \n  > **Availability:** Requires the `aiAssistant` capability. Check `GET /account/capabilities`.\n  ",
        "operationId": "createDocument",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "fileId": "00000000-0000-0000-0000-000000000000",
                "name": "Contract",
                "signeeDetails": [
                  {
                    "fullName": "Jane Doe",
                    "emailAddress": "jane.doe@example.com",
                    "signaturePlacement": "new_page"
                  }
                ]
              },
              "schema": {
                "type": "object",
                "oneOf": [
                  {
                    "required": [
                      "fileId"
                    ],
                    "properties": {
                      "fileId": {
                        "type": "string",
                        "format": "uuid",
                        "description": "The ID of the uploaded file. Mutually exclusive with `templateId`."
                      }
                    }
                  },
                  {
                    "required": [
                      "templateId"
                    ],
                    "properties": {
                      "templateId": {
                        "type": "string",
                        "format": "uuid",
                        "description": "The ID of the template to use for creating the document. \nMutually exclusive with `fileId`. \nRequires the `templates` capability. Check `GET /account/capabilities`.\""
                      }
                    }
                  }
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Name of the document to be signed"
                  },
                  "userId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional User ID to assign as the document owner. If omitted, the authenticated user will be the owner."
                  },
                  "signeeDetails": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/SignatureField"
                    },
                    "minItems": 1,
                    "maxItems": 18
                  },
                  "enableSigningOrder": {
                    "type": "boolean",
                    "default": false,
                    "description": "Enables sequential signing order for the document. When set to `true`, signees will be invited to sign based on their `signingOrder` value in the `signeeDetails` array.\n\n- **`false`** (default): All signees can sign simultaneously and receive invitations immediately\n- **`true`**: Signees are invited based on their `signingOrder` value. Multiple signees with the same order value can sign simultaneously\n\nWhen enabled, signees will only receive their invitation (email/SMS/WhatsApp) when it's their turn to sign."
                  },
                  "fields": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/FormFieldValue"
                    }
                  },
                  "fieldsReadonlyMode": {
                    "type": "string",
                    "enum": [
                      "keepOriginal",
                      "filled",
                      "all"
                    ],
                    "default": "keepOriginal",
                    "description": "Determines how form fields are handled in terms of read-only settings."
                  },
                  "language": {
                    "type": "string",
                    "enum": [
                      "en",
                      "sv",
                      "es"
                    ],
                    "default": "en",
                    "description": "Language of the signature request"
                  },
                  "sharingSetting": {
                    "type": "string",
                    "enum": [
                      "private",
                      "shared"
                    ],
                    "description": "Sharing setting for sharing the document between users within the company account. The default value depends on the account setting."
                  },
                  "personalMessage": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Optional message to be included in the invitation sent to signees. This message can provide additional context or instructions.  \nThe message is plain text and will be displayed in the email or SMS/WhatsApp invitation (where supported).\nThe same message will be used for all signees, if more than one."
                  },
                  "aiAssistant": {
                    "$ref": "#/components/schemas/AiAssistant"
                  },
                  "errorLanguage": {
                    "type": "string",
                    "enum": [
                      "en",
                      "sv",
                      "es"
                    ],
                    "description": "Language for the error message"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Document created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "documentId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Unique identifier of the created document"
                    },
                    "name": {
                      "type": "string",
                      "description": "Name of the document"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Document creation timestamp"
                    },
                    "userId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "User ID of the document owner"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "created",
                        "processing"
                      ],
                      "description": "Status of the document creation:\n- `created`: Document is ready and invitations have been sent to signees\n- `processing`: Document is being processed asynchronously"
                    },
                    "documentUrl": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "Link to the sender's/owner's document overview page in Formify.\nThis field will be `null` when `status` is `processing` (e.g., when AI Assistant is enabled), as the document URL is not available until processing is complete."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Not Found - File or template not found or already deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/docs\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"fileId\": \"00000000-0000-0000-0000-000000000000\",\n  \"name\": \"Contract\",\n  \"signeeDetails\": [\n    {\n      \"fullName\": \"Jane Doe\",\n      \"emailAddress\": \"jane.doe@example.com\",\n      \"signaturePlacement\": \"new_page\"\n    }\n  ]\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"fileId\": \"00000000-0000-0000-0000-000000000000\",\n    \"name\": \"Contract\",\n    \"signeeDetails\": [\n        {\n            \"fullName\": \"Jane Doe\",\n            \"emailAddress\": \"jane.doe@example.com\",\n            \"signaturePlacement\": \"new_page\"\n        }\n    ]\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = {\n    \"fileId\": \"00000000-0000-0000-0000-000000000000\",\n    \"name\": \"Contract\",\n    \"signeeDetails\": [\n        {\n            \"fullName\": \"Jane Doe\",\n            \"emailAddress\": \"jane.doe@example.com\",\n            \"signaturePlacement\": \"new_page\"\n        }\n    ]\n}\n\nresponse = requests.post(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      },
      "get": {
        "tags": [
          "docs"
        ],
        "summary": "Get documents",
        "description": "Get documents created by the user or shared within the account.",
        "operationId": "getDocuments",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 50
            },
            "description": "Number of documents to retrieve per request (max 50)"
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Number of items to skip before starting to collect the result set"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "200": {
            "description": "List of documents fetched successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "limit": {
                      "type": "integer",
                      "description": "The maximum number of documents requested per page",
                      "example": 20
                    },
                    "offset": {
                      "type": "integer",
                      "description": "The offset used in the current request",
                      "example": 0
                    },
                    "nextOffset": {
                      "type": "integer",
                      "nullable": true,
                      "description": "The next offset for pagination, or null if there are no more results",
                      "example": null
                    },
                    "documents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "documentId": {
                            "type": "string",
                            "format": "uuid",
                            "description": "Document ID"
                          },
                          "name": {
                            "type": "string",
                            "description": "Name of the document"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Document creation timestamp"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "created",
                              "processing",
                              "awaiting_signatures",
                              "completed",
                              "revoked",
                              "deleted"
                            ],
                            "description": "Current status of the document:\n- `created`: Document has been created and is ready\n- `processing`: Document is being processed asynchronously\n- `awaiting_signatures`: Document is awaiting signatures\n- `completed`: All signatures collected\n- `revoked`: Document revoked\n- `deleted`: Document deleted"
                          },
                          "userId": {
                            "type": "string",
                            "format": "uuid",
                            "description": "User ID of the document owner"
                          }
                        }
                      }
                    },
                    "errorLanguage": {
                      "type": "string",
                      "enum": [
                        "en",
                        "sv",
                        "es"
                      ],
                      "description": "Language for the error message"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/{documentId}/signature-field": {
      "patch": {
        "tags": [
          "docs"
        ],
        "summary": "Update signee details for a document",
        "description": "Updates signee details for an existing document.  \n\n### Use Case:\nUpdating the contact information for a signee (without updating the document content) is typically done when the sender has entered the wrong email address or phone number, preventing the signing party from accessing the document. It could also be done when the signing party has to be delegated to/replaced by another party.\nWhen there are updates to the content required, you must instead revoke the document and send a new one.\n\n**Notes:**\n- It's only possible to edit contact details, not to add new contact methods.\n- If **emailAddress** or **phoneNumber** is updated, a new document invitation will be sent to the signee with the updated contact details.\n- A **new `signeeId`** will be generated for the updated signee within this document.\n- Any previously sent document links to the old contact information will be **automatically revoked** to ensure security.\n- The document must not be completed or signed by the signee; otherwise, the request will fail with a `403 Forbidden` error.\n\n### Cooldown Rules\n- **Update Limit**: A signee's contact details can only be updated a maximum of two times, before a **60-minute cooldown period** applies.\n- **Cooldown**: After updating a signature field twice, a **60-minute cooldown period** applies before another update can be made for the same signature field.\n- **Checking Remaining Cooldown**: The remaining cooldown time before another update is allowed is included in the response as `updates.updateCooldown` property in the signee details when retrieving document information via `GET /docs/{documentId}`.",
        "operationId": "updateSignatureFields",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the document."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "signeeId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Unique identifier of the signee to update."
                    },
                    "fullName": {
                      "type": "string",
                      "nullable": true,
                      "description": "Updated full name of the signee. If null, the name remains unchanged."
                    },
                    "emailAddress": {
                      "type": "string",
                      "format": "email",
                      "nullable": true,
                      "description": "Updated email address of the signee. If null, the email remains unchanged."
                    },
                    "phoneNumber": {
                      "type": "string",
                      "nullable": true,
                      "description": "Updated phone number of the signee. If null, the phone number remains unchanged."
                    }
                  }
                }
              },
              "example": [
                {
                  "signeeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "fullName": "John Doe",
                  "emailAddress": "john.doe@example.com",
                  "phoneNumber": "+46701234567"
                }
              ]
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signee details updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "signeeId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Unique identifier of the updated signee."
                    },
                    "updated": {
                      "type": "object",
                      "properties": {
                        "fullName": {
                          "type": "string",
                          "description": "Updated full name.",
                          "nullable": true
                        },
                        "emailAddress": {
                          "type": "string",
                          "format": "email",
                          "description": "Updated email address.",
                          "nullable": true
                        },
                        "phoneNumber": {
                          "type": "string",
                          "description": "Updated phone number.",
                          "nullable": true
                        }
                      }
                    },
                    "updateCooldown": {
                      "type": "integer",
                      "description": "Remaining cooldown time in seconds before another signee update can be made."
                    }
                  }
                },
                "example": {
                  "signeeId": "9c8704a4-2c62-4837-9b98-b27c00cd920f",
                  "updated": {
                    "emailAddress": "john.doe@example.com"
                  },
                  "updateCooldown": 3600
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - The document is completed or the signee has already signed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Cannot update signees for a completed document."
                    },
                    "code": {
                      "type": "integer",
                      "example": 403
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Document or signee not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "429": {
            "description": "Too many updates - Cooldown period applies.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Update limit reached, try again later. Remaining cooldown: 3600 seconds"
                    },
                    "code": {
                      "type": "integer",
                      "example": 429
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X PATCH \"https://docs-api.formify.eu/v1/docs/{documentId}/signature-field\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '[\n  {\n    \"signeeId\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n    \"fullName\": \"John Doe\",\n    \"emailAddress\": \"john.doe@example.com\",\n    \"phoneNumber\": \"+46701234567\"\n  }\n]'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}/signature-field\", {\n  method: \"PATCH\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify([\n    {\n        \"signeeId\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n        \"fullName\": \"John Doe\",\n        \"emailAddress\": \"john.doe@example.com\",\n        \"phoneNumber\": \"+46701234567\"\n    }\n])\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}/signature-field\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = [\n    {\n        \"signeeId\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n        \"fullName\": \"John Doe\",\n        \"emailAddress\": \"john.doe@example.com\",\n        \"phoneNumber\": \"+46701234567\"\n    }\n]\n\nresponse = requests.patch(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/{documentId}/reminder": {
      "post": {
        "tags": [
          "docs"
        ],
        "summary": "Send reminders to signees who haven't signed",
        "description": "Sends reminders to the specified signees for a document that is awaiting signatures.\n\n**Required body parameter:** `signeeIds` — an array of signee IDs to remind. See below for how to obtain these IDs.\n\n### How to get signeeIds\n\nThe `signeeIds` you pass here are the `signeeId` values returned in `signeeDetails` from `GET /docs/{documentId}`:\n\n```\nGET /docs/{documentId}\n→ response.signeeDetails[].signeeId   ← use this as signeeIds input\n```\n\nEach signee who has not yet signed will have a `signeeId` in that array. Pass one or more of those IDs to this endpoint to send them a reminder.\n\n### Rate limit — important\n\n> **Max 2 reminders per signee per 60-minute rolling window.**\n> After the limit is reached, a `429 Too Many Requests` response is returned.\n> Check `reminderCooldown` (in seconds) in the response or in `GET /docs/{documentId}` → `signeeDetails[].reminders.reminderCooldown` to see when the next reminder can be sent.\n\n### Notes\n- The document must have status `awaiting_signatures`. Any other status results in `403 Forbidden`.\n- Reminders are sent via all configured contact methods (email, SMS, WhatsApp) for each signee.\n- SMS/WhatsApp reminders may incur fees. Ensure the account has sufficient balance.\n- The response includes a breakdown of which reminders were sent (`remindersSentTo`) and which were not (`remindersNotSentTo`), with reasons for any failures.",
        "operationId": "sendReminder",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the document."
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "errorLanguage": {
                    "type": "string",
                    "enum": [
                      "en",
                      "sv",
                      "es"
                    ],
                    "description": "Language for the error message."
                  },
                  "signeeIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "List of signee IDs to remind. Obtain these from `signeeDetails[].signeeId` in the `GET /docs/{documentId}` response. **Required.**"
                  }
                },
                "required": [
                  "signeeIds"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Reminders processed successfully. Contains lists of signee IDs for whom reminders were successfully sent (`remindersSentTo`) \nand those who could not be reminded (`remindersNotSentTo`) with reasons for failure.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "remindersSentTo": {
                      "type": "array",
                      "description": "List of signee IDs who were successfully reminded.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "signeeId": {
                            "type": "string",
                            "format": "uuid",
                            "description": "The ID of the signee who was reminded."
                          },
                          "reminderCooldown": {
                            "type": "integer",
                            "description": "Remaining cooldown time in seconds before the next reminder can be sent."
                          }
                        }
                      }
                    },
                    "remindersNotSentTo": {
                      "type": "array",
                      "description": "List of signee IDs who could not be reminded, with reasons.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "signeeId": {
                            "type": "string",
                            "format": "uuid",
                            "description": "The ID of the signee who could not be reminded."
                          },
                          "message": {
                            "type": "string",
                            "description": "Reason why the signee could not be reminded."
                          },
                          "reminderCooldown": {
                            "type": "integer",
                            "description": "Remaining cooldown time in seconds before the next reminder can be sent."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters, such as missing or invalid `signeeIds`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "402": {
            "description": "Payment required. The account does not have enough balance to cover SMS/WhatsApp fees for sending reminders.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. Possible reasons:\n- The document is revoked or completed.\n- The user does not have permission to access the document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "Document not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "429": {
            "description": "Too many reminders - Cooldown period applies.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Reminder limit reached, try again later. Remaining cooldown: 3600 seconds"
                    },
                    "code": {
                      "type": "integer",
                      "example": 429
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error while processing the reminder request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/docs/{documentId}/reminder\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"errorLanguage\": \"en\",\n  \"signeeIds\": [\n    \"00000000-0000-0000-0000-000000000000\"\n  ]\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}/reminder\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"errorLanguage\": \"en\",\n    \"signeeIds\": [\n        \"00000000-0000-0000-0000-000000000000\"\n    ]\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}/reminder\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = {\n    \"errorLanguage\": \"en\",\n    \"signeeIds\": [\n        \"00000000-0000-0000-0000-000000000000\"\n    ]\n}\n\nresponse = requests.post(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/{documentId}/revoke": {
      "put": {
        "tags": [
          "docs"
        ],
        "summary": "Revoke a document",
        "description": "Revoke a document that has been sent for signing. This operation is supported only if the document has not yet been fully signed. A revoked document can no longer be opened or signed, but it's not immediately deleted from the system.",
        "operationId": "revokeDocumentById",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the document to revoke "
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "errorLanguage": {
                    "type": "string",
                    "enum": [
                      "en",
                      "sv",
                      "es"
                    ],
                    "description": "Language for the error message"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Document revoked successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "documentId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The unique identifier of the revoked document."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Document not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X PUT \"https://docs-api.formify.eu/v1/docs/{documentId}/revoke\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"errorLanguage\": \"en\"\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}/revoke\", {\n  method: \"PUT\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"errorLanguage\": \"en\"\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}/revoke\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = {\n    \"errorLanguage\": \"en\"\n}\n\nresponse = requests.put(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/{documentId}/signed-file": {
      "get": {
        "tags": [
          "docs"
        ],
        "summary": "Redirect to a URL for the signed PDF file download",
        "description": "This endpoint generates a pre-signed URL for securely downloading a signed PDF file, valid for 10 minutes. It responds with a 302 status and a 'Location' header containing the URL for the client to follow.",
        "operationId": "downloadSignedFile",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "description": "ID of the document to download the signed file for",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the pre-signed URL for the signed file",
            "headers": {
              "Location": {
                "description": "URL to download the signed file",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Invalid documentId or document is not signed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Document not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/{documentId}/signed-file\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}/signed-file\", {\n  method: \"GET\",\n  redirect: \"manual\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconsole.log(response.status);    // 302\nconsole.log(response.headers.get(\"Location\")); // pre-signed download URL"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}/signed-file\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers, allow_redirects=False)\nprint(response.status_code)           # 302\nprint(response.headers[\"Location\"])   # pre-signed download URL"
          }
        ]
      }
    },
    "/docs/{documentId}": {
      "get": {
        "tags": [
          "docs"
        ],
        "summary": "Get document details/status",
        "description": "Retrieves detailed information about a specific document, including its status, associated signatures, \nand metadata. \n\nThis endpoint is useful for tracking the progress and current state of a document, as well as for \naccessing specific metadata required for other operations.        ",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the document."
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "200": {
            "description": "Document details retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Name associated with the document link"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Timestamp when the document was created"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Timestamp when the document was last updated"
                    },
                    "completedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Timestamp when the document was completed"
                    },
                    "revokedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Timestamp when the document was revoked"
                    },
                    "expiryDate": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Timestamp when the document will expire, if applicable"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "created",
                        "processing",
                        "error",
                        "awaiting_signatures",
                        "completed",
                        "revoked",
                        "deleted"
                      ],
                      "description": "Status identifier for the document:\n- `created`: Document has been created and is ready\n- `processing`: The document is being processed by the AI Assistant and is not yet ready.\n- `error`: An error occurred during processing (e.g., AI Assistant failed).\n- `awaiting_signatures`: Document is awaiting signatures from one or more signees\n- `completed`: All signatures have been collected\n- `revoked`: Document has been revoked\n- `deleted`: Document has been deleted"
                    },
                    "aiAssistant": {
                      "$ref": "#/components/schemas/AiAssistantResponse",
                      "description": "AI Assistant settings for this document. Only present if AI Assistant was enabled when the document was created. Returns `null` if AI Assistant was not used."
                    },
                    "formifyTransactionId": {
                      "type": "string",
                      "description": "Transaction ID associated with Formify"
                    },
                    "language": {
                      "type": "string",
                      "enum": [
                        "en",
                        "sv",
                        "es"
                      ],
                      "description": "Language code of the document"
                    },
                    "enableSigningOrder": {
                      "type": "boolean",
                      "description": "Indicates whether sequential signing order is enabled for this document. When true, signees are invited based on their signingOrder value."
                    },
                    "signeeDetails": {
                      "type": "array",
                      "description": "List of signees and their statuses",
                      "items": {
                        "type": "object",
                        "properties": {
                          "signeeId": {
                            "type": "string",
                            "format": "uuid",
                            "description": "Unique identifier of the signee"
                          },
                          "fullName": {
                            "type": "string",
                            "description": "Full name of the signee"
                          },
                          "emailAddress": {
                            "type": "string",
                            "format": "email",
                            "description": "Email address of the signee"
                          },
                          "phoneNumber": {
                            "type": "string",
                            "description": "Phone number of the signee used for SMS"
                          },
                          "signatureType": {
                            "type": "string",
                            "enum": [
                              "not_set",
                              "bankid_identification",
                              "digital_ink",
                              "digital_ink_id_scan",
                              "face_liveness"
                            ],
                            "description": "Type of signature"
                          },
                          "signaturePlacement": {
                            "type": "string",
                            "enum": [
                              "existing",
                              "new_page"
                            ],
                            "default": "existing",
                            "description": "Option used to place the signature box on an existing page ('existing', default) or adding a new signature page (A4 portrait format) at the end of the document ('new_page')."
                          },
                          "signingOrder": {
                            "type": "integer",
                            "minimum": 1,
                            "description": "The signing order assigned to this signee. Only relevant when signing order is enabled for the document. Signees with the same order value can sign simultaneously."
                          },
                          "signatureStatus": {
                            "type": "string",
                            "enum": [
                              "signed",
                              "awaiting_signature"
                            ],
                            "description": "Current signature status for this signee:\n- `awaiting_signature`: The signee has not yet signed.\n- `signed`: The signee has completed signing."
                          },
                          "signedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true,
                            "description": "Timestamp when the signee signed the document, if applicable"
                          },
                          "reminders": {
                            "type": "object",
                            "description": "Information about reminders sent to the signee",
                            "properties": {
                              "reminderSentCount": {
                                "type": "integer",
                                "description": "Number of reminders sent to this signee"
                              },
                              "lastReminderSentAt": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true,
                                "description": "Timestamp of the most recent reminder sent to the signee, if applicable"
                              },
                              "reminderCooldown": {
                                "type": "integer",
                                "description": "Number of seconds remaining until a new reminder can be sent to the signee, or 0 if reminders can be sent immediately"
                              }
                            }
                          },
                          "updates": {
                            "type": "object",
                            "description": "Information about updates of the signee",
                            "properties": {
                              "lastUpdateAt": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true,
                                "description": "Timestamp of the most recent update of the signee, if applicable"
                              },
                              "updateCooldown": {
                                "type": "integer",
                                "description": "Number of seconds remaining until a new update of the signee can be made, or 0 if updates can be done immediately"
                              }
                            }
                          }
                        }
                      }
                    },
                    "userId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "User ID of the document owner"
                    },
                    "fileId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "File ID used to create the document"
                    },
                    "documentUrl": {
                      "type": "string",
                      "format": "uri",
                      "description": "Link to the document overview page in Formify"
                    },
                    "templateId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Template ID used, if the document was created based on a template"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/{documentId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      },
      "delete": {
        "tags": [
          "docs"
        ],
        "summary": "Delete a document",
        "description": "Delete a document and its related file. This operation is only allowed if the document is completed or revoked. The file used to create the document signature request will be deleted only if it is not used in other documents.",
        "operationId": "deleteDocumentById",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the document to delete"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "204": {
            "description": "Document successfully deleted"
          },
          "400": {
            "description": "Invalid request parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Not Found - Document not found or already deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X DELETE \"https://docs-api.formify.eu/v1/docs/{documentId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}\", {\n  method: \"DELETE\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconsole.log(response.status); // 204"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.delete(url, headers=headers)\nprint(response.status_code)  # 204"
          }
        ]
      }
    },
    "/docs/{documentId}/fields": {
      "get": {
        "tags": [
          "docs"
        ],
        "summary": "Retrieve all form fields for a document",
        "description": "Retrieves all the form fields defined for a specific document. This endpoint is particularly useful for extracting form field metadata before processing or integration.\n\nThis endpoint is not for getting form field values. For that, use `/docs/{documentId}/field-values`.\n\n### Supported Form Field Types\nThe following form field types are possible:\n- **checkbox**: A field that allows a binary selection (checked or unchecked).\n- **combobox**: A dropdown field allowing selection of one option.\n- **listbox**: A list field allowing selection of one or multiple options.\n- **radioButton**: A field that allows a single selection from a group of options.\n- **textField**: A single-line input field for text.\n\n### Notes:\n- **Required Fields**: A field is marked as required if the `required` property is `true`. This property is determined by the field's metadata.\n- **Read-Only Fields**: A field is marked as read-only if the `readOnly` property is `true`. Such fields cannot be modified by the end user, but can be modified through the API before a document has been sent for signing.\n- **Editable Fields**: A field can be entered/edited through the API if the `editable` property is `true`. For that, use `POST /docs/{documentId}/field-values`        \n- **Bounding Box**: The `boundingBox` property provides the position and dimensions of the form field on the document. The coordinates are measured in points.\n- **Default Values**: For applicable fields, `defaultValues` contains the default options or input values defined in the document.\n- **Options**: Dropdowns, radio buttons, and similar fields include a list of options with `label` and `value` pairs.\n\n### Form Field Name as Key\n- **Key Usage**: The `name` property is used as the key for interacting with form field values instead of `fieldId`.\n- **Reason**: Using the `name` aligns with PDF standards and ensures compatibility with existing PDF workflows and tools.\n- **Advantages**:\n  - **Consistency**: `name` is the standard identifier for form fields in PDFs.\n  - **Human-Readable**: Names are easier to interpret compared to arbitrary IDs.\n  - **Integration-Friendly**: Enables seamless integration with PDF tools that use names as primary identifiers.\n- **`fieldId` Purpose**: While the `fieldId` is included in the metadata for internal purposes, it is not used as the primary identifier in API interactions.",
        "operationId": "getDocumentFields",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "description": "The unique identifier of the document.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved form fields.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FormField"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Document not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/{documentId}/fields\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}/fields\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}/fields\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/{documentId}/field-values": {
      "get": {
        "tags": [
          "docs"
        ],
        "summary": "Retrieve form field values for a completed document",
        "description": "Retrieves all form field values for a specific completed document. \n\nThis endpoint is only supported for **completed documents**. If the document is not completed, the request will fail with a `403 Forbidden` error. \n\n### Notes:\n- **Non-Empty Fields**: Only fields with non-empty values will be included in the response.\n- **Value Format**: The `value` property is always an array of strings. For fields with a single value, the array will contain one element. For fields like checkboxes or multi-select dropdowns, the array may contain multiple elements.\n\n### Example Response:\n```json\n[\n  {\n    \"name\": \"email\",\n    \"value\": [\"test@example.com\"]\n  },\n  {\n    \"name\": \"interests\",\n    \"value\": [\"Photography\", \"Travel\"]\n  }\n]\n```\n\nThe response does not include metadata or structural details about the form fields. For that, use `/docs/{documentId}/fields`.",
        "operationId": "getFieldValues",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "description": "The unique identifier of the document.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved field values for the document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "The name of the form field."
                      },
                      "value": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "description": "An array of strings representing the field values. \n- For single-value fields (e.g., text fields), the array will have one element.\n- For multi-select fields (e.g., checkboxes), the array can have multiple elements.",
                        "example": [
                          "test@example.com"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Access to the document is denied or the document is not completed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Unauthorized access to the file."
                    },
                    "code": {
                      "type": "integer",
                      "example": 403
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The document file was not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/{documentId}/field-values\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}/field-values\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}/field-values\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      },
      "post": {
        "tags": [
          "docs"
        ],
        "summary": "Set form field values for a document",
        "description": "Updates the values of one or more form fields for a specific document.\n\n### Usage:\n- This endpoint **only allows updating form fields**.\n- The document must **not be completed or signed by anyone**; otherwise, the request will fail with a `403 Forbidden` error.\n- Only fields where `editable = true` (from `GET /docs/{documentId}/fields`) can be updated.\n\n### Dynamic Value Format:\n- The `value` property must match the expected format of the field.\n- **Single-value fields (e.g., text fields, radio buttons):** Provide a **single string**.\n- **Multi-value fields (e.g., checkboxes, listboxes):** Provide an **array of strings**, even if it only contains one string value.\n- To clear the values in a listbox or combobox, provide an array including a single empty string.\n- For radio buttons, an empty string clears the selection.\n- Get the expected format (and options, when available) for each field type using `GET /docs/{documentId}/fields`.\n\n### Supported Field Types and Expected Formats:\n\n| Field Type          | Description                     | Example Format |\n|---------------------|---------------------------------|----------------|\n| `textField`         | A standard text input field.   | `\"value\": \"John Doe\"` |\n| `checkbox`          | A field supporting multiple selections (on/off). | `\"value\": [\"Yes\"]` |\n| `radioButton`       | A field where only one option can be selected. | `\"value\": \"Red\"` or `\"value\": \"\"` to clear selection |\n| `listbox`           | A list where users can select one or multiple options. | `\"value\": [\"Option1\", \"Option2\"]` or `\"value\": [\"\"]` to clear values |\n| `combobox`          | A dropdown field allowing selection of one option. | `\"value\": [\"SelectedOption\"]` or `\"value\": [\"\"]` to clear values  |",
        "operationId": "setFieldValues",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "description": "The unique identifier of the document.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fields": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/FormFieldValue"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated form field values.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters or field constraints violated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Invalid field value provided."
                    },
                    "code": {
                      "type": "integer",
                      "example": 400
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - The document is completed and cannot be modified.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "message": {
                      "type": "string",
                      "example": "Cannot update field values for a completed document."
                    },
                    "code": {
                      "type": "integer",
                      "example": 403
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The document or field was not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/docs/{documentId}/field-values\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"fields\": [\n    {\n      \"name\": \"email\",\n      \"value\": \"test@example.com\"\n    }\n  ]\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/{documentId}/field-values\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"fields\": [\n        {\n            \"name\": \"email\",\n            \"value\": \"test@example.com\"\n        }\n    ]\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/{documentId}/field-values\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = {\n    \"fields\": [\n        {\n            \"name\": \"email\",\n            \"value\": \"test@example.com\"\n        }\n    ]\n}\n\nresponse = requests.post(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/drafts": {
      "post": {
        "tags": [
          "drafts"
        ],
        "summary": "Create a draft",
        "description": "Creates a new draft document without sending it for signing. The draft stores all configuration\nand can be updated, sent, or deleted later.\n\n### Requirements\n- `fileId` is **required**. Use `POST /files` to upload a file beforehand.\n- Unlike `POST /docs`, signee details are optional — you can create an empty draft and add signees later via `PUT /docs/drafts/{draftId}`.\n- `templateId` is not supported for drafts. Use `fileId` only.\n\n### Ownership\nThe `userId` parameter can optionally be set to assign draft ownership to a different user within the same account. If omitted, the authenticated user will be the owner.",
        "operationId": "createDraft",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "fileId": "00000000-0000-0000-0000-000000000000",
                "name": "Contract Draft",
                "signeeDetails": [
                  {
                    "fullName": "Jane Doe",
                    "emailAddress": "jane.doe@example.com",
                    "signatureType": "digital_ink",
                    "signaturePlacement": "existing",
                    "signatureBox": {
                      "x": 376,
                      "y": 784,
                      "page": 0,
                      "scale": 0.5
                    },
                    "signingOrder": 1
                  }
                ],
                "language": "en",
                "sharingSetting": "private",
                "errorLanguage": "en"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateDraftRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Draft created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftCreatedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters (missing field, wrong format, etc.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Insufficient access (not owner, missing capability).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "File not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "write"
            ]
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/docs/drafts\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"fileId\": \"00000000-0000-0000-0000-000000000000\",\n  \"name\": \"Contract Draft\",\n  \"signeeDetails\": [\n    {\n      \"fullName\": \"Jane Doe\",\n      \"emailAddress\": \"jane.doe@example.com\",\n      \"signatureType\": \"digital_ink\",\n      \"signaturePlacement\": \"existing\",\n      \"signatureBox\": {\n        \"x\": 376,\n        \"y\": 784,\n        \"page\": 0,\n        \"scale\": 0.5\n      },\n      \"signingOrder\": 1\n    }\n  ],\n  \"language\": \"en\",\n  \"sharingSetting\": \"private\",\n  \"errorLanguage\": \"en\"\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"fileId\": \"00000000-0000-0000-0000-000000000000\",\n    \"name\": \"Contract Draft\",\n    \"signeeDetails\": [\n        {\n            \"fullName\": \"Jane Doe\",\n            \"emailAddress\": \"jane.doe@example.com\",\n            \"signatureType\": \"digital_ink\",\n            \"signaturePlacement\": \"existing\",\n            \"signatureBox\": {\n                \"x\": 376,\n                \"y\": 784,\n                \"page\": 0,\n                \"scale\": 0.5\n            },\n            \"signingOrder\": 1\n        }\n    ],\n    \"language\": \"en\",\n    \"sharingSetting\": \"private\",\n    \"errorLanguage\": \"en\"\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = {\n    \"fileId\": \"00000000-0000-0000-0000-000000000000\",\n    \"name\": \"Contract Draft\",\n    \"signeeDetails\": [\n        {\n            \"fullName\": \"Jane Doe\",\n            \"emailAddress\": \"jane.doe@example.com\",\n            \"signatureType\": \"digital_ink\",\n            \"signaturePlacement\": \"existing\",\n            \"signatureBox\": {\n                \"x\": 376,\n                \"y\": 784,\n                \"page\": 0,\n                \"scale\": 0.5\n            },\n            \"signingOrder\": 1\n        }\n    ],\n    \"language\": \"en\",\n    \"sharingSetting\": \"private\",\n    \"errorLanguage\": \"en\"\n}\n\nresponse = requests.post(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      },
      "get": {
        "tags": [
          "drafts"
        ],
        "summary": "List drafts",
        "description": "Retrieves a paginated list of drafts owned by the authenticated user or a specified user within the same account.",
        "operationId": "listDrafts",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 50
            },
            "description": "Number of drafts per page (1–50). Default: 10"
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Number of items to skip. Default: 0"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for error messages"
          },
          {
            "in": "query",
            "name": "userId",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "List drafts for a specific user within the same account. If omitted, lists the authenticated user's drafts."
          }
        ],
        "responses": {
          "200": {
            "description": "List of drafts fetched successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Insufficient access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "read"
            ]
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/drafts\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/drafts/{draftId}": {
      "put": {
        "tags": [
          "drafts"
        ],
        "summary": "Update a draft",
        "description": "Updates an existing draft. All properties from the create request can be modified.\nWhen updating, the full draft configuration is replaced — any signee details not included\nin the update request will be removed.\n\n### Notes\n- The draft must have status `draft`. Expired or sent drafts cannot be updated.\n- `fileId` is optional when updating. If omitted, the draft keeps its current file.\n- All other fields follow the same rules as `POST /docs/drafts`.",
        "operationId": "updateDraft",
        "parameters": [
          {
            "in": "path",
            "name": "draftId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the draft to update"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "name": "Updated Contract Draft",
                "signeeDetails": [
                  {
                    "fullName": "Jane Doe",
                    "emailAddress": "jane.doe@example.com",
                    "signatureType": "digital_ink",
                    "signaturePlacement": "new_page",
                    "signingOrder": 1
                  },
                  {
                    "fullName": "John Smith",
                    "emailAddress": "john.smith@example.com",
                    "signatureType": "digital_ink",
                    "signaturePlacement": "new_page",
                    "signingOrder": 2
                  }
                ],
                "enableSigningOrder": true,
                "errorLanguage": "en"
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateDraftRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Draft updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftCreatedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters (expired draft, wrong format, etc.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Insufficient access (not owner, missing capability).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "Draft not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "write"
            ]
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X PUT \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"name\": \"Updated Contract Draft\",\n  \"signeeDetails\": [\n    {\n      \"fullName\": \"Jane Doe\",\n      \"emailAddress\": \"jane.doe@example.com\",\n      \"signatureType\": \"digital_ink\",\n      \"signaturePlacement\": \"new_page\",\n      \"signingOrder\": 1\n    },\n    {\n      \"fullName\": \"John Smith\",\n      \"emailAddress\": \"john.smith@example.com\",\n      \"signatureType\": \"digital_ink\",\n      \"signaturePlacement\": \"new_page\",\n      \"signingOrder\": 2\n    }\n  ],\n  \"enableSigningOrder\": true,\n  \"errorLanguage\": \"en\"\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\", {\n  method: \"PUT\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"name\": \"Updated Contract Draft\",\n    \"signeeDetails\": [\n        {\n            \"fullName\": \"Jane Doe\",\n            \"emailAddress\": \"jane.doe@example.com\",\n            \"signatureType\": \"digital_ink\",\n            \"signaturePlacement\": \"new_page\",\n            \"signingOrder\": 1\n        },\n        {\n            \"fullName\": \"John Smith\",\n            \"emailAddress\": \"john.smith@example.com\",\n            \"signatureType\": \"digital_ink\",\n            \"signaturePlacement\": \"new_page\",\n            \"signingOrder\": 2\n        }\n    ],\n    \"enableSigningOrder\": true,\n    \"errorLanguage\": \"en\"\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = {\n    \"name\": \"Updated Contract Draft\",\n    \"signeeDetails\": [\n        {\n            \"fullName\": \"Jane Doe\",\n            \"emailAddress\": \"jane.doe@example.com\",\n            \"signatureType\": \"digital_ink\",\n            \"signaturePlacement\": \"new_page\",\n            \"signingOrder\": 1\n        },\n        {\n            \"fullName\": \"John Smith\",\n            \"emailAddress\": \"john.smith@example.com\",\n            \"signatureType\": \"digital_ink\",\n            \"signaturePlacement\": \"new_page\",\n            \"signingOrder\": 2\n        }\n    ],\n    \"enableSigningOrder\": true,\n    \"errorLanguage\": \"en\"\n}\n\nresponse = requests.put(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      },
      "get": {
        "tags": [
          "drafts"
        ],
        "summary": "Get draft details",
        "description": "Retrieves detailed information about a specific draft, including signee details,\nsignature placements, and AI assistant configuration.\n\n### Notes\n- The draft must have status `draft`. Expired drafts return a `400 Bad Request`.\n- The response includes the full draft configuration as it was last saved.",
        "operationId": "getDraft",
        "parameters": [
          {
            "in": "path",
            "name": "draftId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the draft"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for error messages"
          }
        ],
        "responses": {
          "200": {
            "description": "Draft details retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftDetailsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (e.g., expired draft).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Insufficient access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "Draft not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "read"
            ]
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      },
      "delete": {
        "tags": [
          "drafts"
        ],
        "summary": "Delete a draft",
        "description": "Permanently deletes a draft and all its associated data (file, signature fields, thumbnails).\nBoth active drafts and expired drafts can be deleted.\n\n### Notes\n- This operation is irreversible.\n- The file used by the draft will be deleted only if it is not used in other documents.",
        "operationId": "deleteDraft",
        "parameters": [
          {
            "in": "path",
            "name": "draftId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the draft to delete"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for error messages"
          }
        ],
        "responses": {
          "204": {
            "description": "Draft deleted successfully. No content returned."
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Insufficient access (not owner).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "Draft not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "write"
            ]
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X DELETE \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\", {\n  method: \"DELETE\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconsole.log(response.status); // 204"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.delete(url, headers=headers)\nprint(response.status_code)  # 204"
          }
        ]
      }
    },
    "/docs/drafts/{draftId}/file": {
      "get": {
        "tags": [
          "drafts"
        ],
        "summary": "Get a preview PDF of the draft",
        "description": "Returns a PDF representation of the draft with signature fields visually placed according to the draft's configuration.\nUseful for previewing the draft before sending it for signing, or to verify that signature fields are placed where expected.\n\n### Behavior\n- Returns the source file's PDF with signature fields rendered where the draft's fields have valid coordinates (`page`, `x`, `y`).\n- Fields without valid coordinates (e.g. not yet placed) are **not** rendered — the endpoint still returns a PDF, but those fields are invisible in the result.\n- The result is cached. Repeated calls without changes to the draft return the cached version directly without regeneration. The cache is invalidated automatically when the draft is updated via `PUT /docs/drafts/{draftId}`.\n\n### Notes\n- Only fields with coordinates are visualized. Fields without placement are not shown in the returned PDF (but remain in the draft data and will be visualized at signing time if they have coordinates by then).\n- The returned file is a snapshot — it is not connected to signing. For the final signed PDF, use `GET /docs/{documentId}/signed-file` after the draft has been sent.",
        "operationId": "getDraftFile",
        "parameters": [
          {
            "in": "path",
            "name": "draftId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the draft"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for error messages"
          }
        ],
        "responses": {
          "200": {
            "description": "Draft PDF returned successfully.",
            "headers": {
              "Content-Disposition": {
                "description": "Attachment header containing the draft's filename, e.g. `attachment; filename=\"My Contract.pdf\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "Binary PDF representation of the draft."
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - `draftId` missing, draft has expired, or document is not a draft (already sent).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Insufficient access (not owner, or token belongs to a different account).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "Draft not found, or the source file could not be located.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - PDF generation or storage retrieval failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "read"
            ]
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/drafts/{draftId}/file-url": {
      "get": {
        "tags": [
          "drafts"
        ],
        "summary": "Get a one-time download URL for the draft PDF",
        "description": "Generates a short-lived, one-time-use download URL for the draft PDF file.\nUseful when the PDF needs to be opened directly in a browser or shared with a download manager that cannot send an `Authorization` header.\n\n### Behavior\n- The returned `downloadUrl` points to `GET /docs/drafts/{draftId}/file-download/{token}` and requires no authentication header — the token in the URL serves as authorization.\n- The token can be used **once**. It is consumed upon successful download; generate a new URL for each download.\n- The token is valid for **10 minutes** from creation.\n- The token is bound to the specific `draftId` it was created for.\n\n### Notes\n- For server-to-server use cases where the client can send a Bearer token, prefer `GET /docs/drafts/{draftId}/file` which streams the PDF directly.",
        "operationId": "getDraftFileUrl",
        "parameters": [
          {
            "in": "path",
            "name": "draftId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the draft"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for error messages"
          }
        ],
        "responses": {
          "200": {
            "description": "Download URL generated successfully.",
            "headers": {
              "Cache-Control": {
                "description": "`private, no-store` — the response must not be cached.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "downloadUrl": {
                      "type": "string",
                      "format": "uri",
                      "description": "One-time-use URL for downloading the draft PDF. No authentication header required."
                    },
                    "fileName": {
                      "type": "string",
                      "description": "Suggested filename for the downloaded PDF."
                    },
                    "mimeType": {
                      "type": "string",
                      "description": "MIME type of the file. Always `application/pdf`."
                    },
                    "expiresInMinutes": {
                      "type": "integer",
                      "description": "Number of minutes until the download URL expires."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - `draftId` missing, draft has expired, or document is not a draft (already sent).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Insufficient access (not owner, or token belongs to a different account).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "Draft not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "read"
            ]
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file-url\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file-url\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file-url\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/drafts/{draftId}/file-download/{token}": {
      "get": {
        "tags": [
          "drafts"
        ],
        "summary": "Download a draft PDF using a one-time token",
        "description": "Streams the draft PDF using a one-time download token previously obtained from `GET /docs/drafts/{draftId}/file-url`.\n\n### Authentication\n**No authentication header is required.** The token in the URL serves as authorization.\n\n### Behavior\n- The token is consumed on successful download — a new token must be generated via `/file-url` for each subsequent download.\n- The token is valid for 10 minutes from creation and only works for the `draftId` it was created for.",
        "operationId": "downloadDraftFileWithToken",
        "parameters": [
          {
            "in": "path",
            "name": "draftId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the draft"
          },
          {
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "One-time download token returned by `GET /docs/drafts/{draftId}/file-url`"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for error messages"
          }
        ],
        "responses": {
          "200": {
            "description": "Draft PDF returned successfully.",
            "headers": {
              "Content-Disposition": {
                "description": "Attachment header containing the draft's filename, e.g. `attachment; filename=\"My Contract.pdf\"; filename*=UTF-8''My%20Contract.pdf`.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`private, no-store` — the response must not be cached.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "Binary PDF representation of the draft."
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - `draftId` or `token` missing, draft has expired, or document is not a draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Token is invalid, expired, already used, or does not match the `draftId`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Draft not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          }
        },
        "security": [],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file-download/{token}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file-download/{token}\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/file-download/{token}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/docs/drafts/{draftId}/send": {
      "post": {
        "tags": [
          "drafts"
        ],
        "summary": "Send a draft",
        "description": "Converts a draft into a live document and sends invitations to all signees.\nOnce sent, the draft becomes a regular document and can no longer be edited as a draft.\n\n### Important — Send Validations\nWhen sending a draft, the same validations as `POST /docs` apply:\n- Each signee **must have at least one contact method** (email or phone).\n- Signature placements using `existing` **must have valid coordinates** (page, x, y).\n- `digital_ink_id_scan` signatures **must have both** `signatureBox` and `idScanBox` coordinates.\n- The signature placement page must exist within the document's page count.\n- If any validation fails, the draft remains unsent and a descriptive error is returned.\n\n### Notes\n- The draft must have status `draft`. Expired drafts cannot be sent.\n- After successful send, the returned `documentId` is the same as the former `draftId`.\n- The document is now available via `GET /docs/{documentId}` and all other `/docs/{documentId}` endpoints.\n- If AI Assistant is enabled, the document will be in `processing` status initially (same as `POST /docs`).",
        "operationId": "sendDraft",
        "parameters": [
          {
            "in": "path",
            "name": "draftId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the draft to send"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for error messages"
          }
        ],
        "responses": {
          "200": {
            "description": "Draft sent successfully. The draft is now a live document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftSentResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (missing contact method, invalid coordinates, expired draft, etc.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - Insufficient access (not owner, missing capability).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "Draft not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "write"
            ]
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/send\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/send\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/docs/drafts/{draftId}/send\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.post(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/templates": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "List all templates",
        "description": "Retrieves a paginated list of all templates available to the user. \n\n### Notes:\n- Templates are reusable documents pre-configured for signature requests.\n- **Template Feature Activation**:  \n  Requires the `templates` capability. Check `GET /account/capabilities`.",
        "operationId": "getTemplates",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 50
            },
            "description": "Number of documents to retrieve per request (max 50)"
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Number of items to skip before starting to collect the result set"
          },
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "200": {
            "description": "List of templates fetched successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "limit": {
                      "type": "integer",
                      "description": "The maximum number of templates requested per page"
                    },
                    "offset": {
                      "type": "integer",
                      "description": "The offset used in the current request"
                    },
                    "nextOffset": {
                      "type": "integer",
                      "nullable": true,
                      "description": "The next offset for pagination, or null if there are no more results"
                    },
                    "templates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "templateId": {
                            "type": "string",
                            "format": "uuid",
                            "description": "Template ID"
                          },
                          "name": {
                            "type": "string",
                            "description": "Name of the template"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Template creation timestamp"
                          },
                          "userId": {
                            "type": "string",
                            "format": "uuid",
                            "description": "User ID of the document owner"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/templates\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/templates\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/templates\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/templates/{templateId}": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Retrieve template details",
        "description": "Retrieves detailed information about a specific template, including its fields, signees, and metadata.\n\n### Notes:\n- Templates contain pre-configured signature fields and signee details.\n- You can use this endpoint to review the template's details before creating a document using the template.",
        "operationId": "getTemplateDetails",
        "parameters": [
          {
            "name": "templateId",
            "in": "path",
            "required": true,
            "description": "The unique identifier of the template.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template details retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "templateId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Unique identifier of the template"
                    },
                    "name": {
                      "type": "string",
                      "description": "Name of the template"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Timestamp when the template was created"
                    },
                    "userId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "User ID of the document owner"
                    },
                    "sharingSetting": {
                      "type": "string",
                      "enum": [
                        "shared",
                        "private"
                      ],
                      "description": "Sharing setting for the template"
                    },
                    "signeeDetails": {
                      "type": "array",
                      "description": "List of signees and their default signature details",
                      "items": {
                        "$ref": "#/components/schemas/SignatureField"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Template not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/templates/{templateId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/templates/{templateId}\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/templates/{templateId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/templates/{templateId}/fields": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Retrieve all form fields for a template",
        "description": "Retrieves all the form fields defined in a specific template. This endpoint is useful for extracting form field metadata before creating a document from the template.\n\n### Supported Form Field Types\nThe following form field types are possible:\n- **checkbox**: A field that allows a binary selection (checked or unchecked).\n- **combobox**: A dropdown field allowing selection of one option.\n- **listbox**: A list field allowing selection of one or multiple options.\n- **radioButton**: A field that allows a single selection from a group of options.\n- **textField**: A single-line input field for text.\n\n### Notes:\n- **Required Fields**: A field is marked as required if the `required` property is `true`.\n- **Read-Only Fields**: A field is marked as read-only if the `readOnly` property is `true`.\n- **Editable Fields**: Fields with `editable = true` can be pre-filled when creating a document.",
        "operationId": "getTemplateFields",
        "parameters": [
          {
            "name": "templateId",
            "in": "path",
            "required": true,
            "description": "The unique identifier of the template.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved form fields for the template.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FormField"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Template not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/templates/{templateId}/fields\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/templates/{templateId}/fields\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/templates/{templateId}/fields\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/account/users": {
      "get": {
        "tags": [
          "account"
        ],
        "summary": "Retrieve list of users on the account",
        "description": "Retrieve a list of users on the account. Invited users with pending invitations are also included in the list (`invited`).",
        "operationId": "getAccountUsers",
        "parameters": [
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "200": {
            "description": "List of account users retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "users": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "userId": {
                            "type": "string",
                            "format": "uuid",
                            "description": "Unique identifier for the user."
                          },
                          "email": {
                            "type": "string",
                            "format": "email",
                            "description": "Email address of the user."
                          },
                          "firstName": {
                            "type": "string",
                            "description": "User's first name."
                          },
                          "lastName": {
                            "type": "string",
                            "description": "User's last name."
                          },
                          "fullName": {
                            "type": "string",
                            "description": "User's full name, if available."
                          },
                          "roles": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "admin",
                                "user"
                              ]
                            },
                            "description": "Roles assigned to the user within the account."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "active",
                              "invited"
                            ],
                            "description": "Status of the user on the account."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Not Found - Account or users resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/account/users\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/account/users\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/account/users\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/account/capabilities": {
      "get": {
        "tags": [
          "account"
        ],
        "summary": "Get account capabilities",
        "description": "Returns which features and add-ons are enabled for the account.\n\nUse this endpoint to check what is available before calling endpoints that require specific capabilities. Attempting to use a capability that is not enabled returns `403 Forbidden`.\n\n### Capabilities\n\n| Capability | Description |\n|---|---|\n| `deliverySms` | `phoneNumberDeliveryMethod: sms` on signee |\n| `deliveryWhatsapp` | `phoneNumberDeliveryMethod: whatsapp` on signee |\n| `templates` | `GET /templates`, using `templateId` on document |\n| `templatesCreate` | `POST /templates` |\n| `signingOrder` | `enableSigningOrder: true` on document |\n| `signAndPay` | Sign & Pay feature |\n| `signAndPayCreate` | Create Sign & Pay links |\n| `signatureBankId` | `bankid_identification` signature type |\n| `signatureIdScan` | `digital_ink_id_scan` signature type |\n| `signatureFaceLiveness` | `face_liveness` signature type |\n| `verificationToOpen` | Verification-to-open setting on documents |\n| `disableSubmit` | Disable submit/sign setting on documents |\n| `searchableFormFields` | Searchable form fields |\n| `apiAccess` | API integration settings |\n| `companyLogo` | Custom company logo |\n| `sharedDocuments` | Shared document settings within account |\n| `verificationEmail` | Email address verification add-on |\n| `verificationPhone` | Mobile number verification add-on |\n| `initials` | Initials add-on |\n| `aiAssistant` | `aiAssistant.enabled: true` on document |\n| `aiAssistantCustomProfile` | Custom AI assistant profile |",
        "operationId": "getAccountCapabilities",
        "parameters": [
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "200": {
            "description": "Account capabilities retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCapabilities"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/account/capabilities\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/account/capabilities\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/account/capabilities\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/user": {
      "get": {
        "tags": [
          "user"
        ],
        "summary": "Get user info settings",
        "description": "Get info about the authenticated user.",
        "operationId": "getUserInfo",
        "parameters": [
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "200": {
            "description": "User info retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "userId": {
                      "type": "string",
                      "description": "Unique identifier of the user"
                    },
                    "emailAddress": {
                      "type": "string",
                      "description": "Email address of the user on this account"
                    },
                    "fullName": {
                      "type": "string",
                      "description": "Full name of the user"
                    },
                    "firstName": {
                      "type": "string",
                      "description": "First name of the user"
                    },
                    "lastName": {
                      "type": "string",
                      "description": "Last name of the user"
                    },
                    "accountId": {
                      "type": "string",
                      "description": "Unique identifier of the user's account"
                    },
                    "accountName": {
                      "type": "string",
                      "description": "Name of the user's account"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/user\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/user\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/user\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/webhooks": {
      "post": {
        "tags": [
          "webhooks"
        ],
        "summary": "Register a new webhook",
        "description": "Register a new webhook to receive event notifications.\n\n### About Webhooks\nWebhooks allow Formify to communicate with your system using callbacks triggered by specified Formify events. Webhooks provide real-time data updates, enabling you to integrate Formify events directly into your workflows.\n\n#### What’s the use for webhooks?\nWith webhooks, you can be notified of specific events to automate workflows such as sending documents for signature or tracking their status in real-time.\n\n#### How do I set up webhooks?\nWhen you create a webhook, specify the events you'd like to subscribe to. To reduce unnecessary requests to your server, only subscribe to the events you plan to handle.\n\n### Entity Types\nFormify uses entity types to filter webhook events based on the scope of data you want to receive. The entity type defines which object changes in Formify trigger your webhook.\n\n| Entity Type       | Description                                                                                                 | Example                                                               |\n|-------------------|-------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|\n| **Document**      | Subscribe to events involving a specific document.                                                          | A document is sent for signature, signed, or completed (sealed). Each change triggers your webhook. |\n\n### Supported Events\n- `document.created`: A document has been created/sent for signature.\n- `document.opened`: A document has been opened.\n- `document.signed`: A document has been signed by a signee.\n- `document.completed`: A document process has been signed by all parties and is sealed.\n- `document.revoked`: A document has been revoked.\n- `document.deleted`: A document has been deleted.\n\n### Notes\n- Other entity types (e.g., `settings.x`) are not yet supported.\n- Do not assume ordering: Events may arrive late, more than once, or out of order\n\n### Webhook Request Payload          \n- See the `DocumentWebhookEvent` schema for the structure of the Document entity type payload.\n\n#### Example for General Events (`document.created`, `document.opened`, etc.)\n```json\n{\n  \"event\": {\n    \"eventId\": \"123e4567-e89b-12d3-a456-426614174000\",\n    \"type\": \"document.created\",\n    \"date\": \"2024-10-25T14:23:55Z\",\n    \"data\": {\n      \"documentId\": \"789e4567-e89b-12d3-a456-426614174111\",\n      \"accountId\": \"456e4567-e89b-12d3-a456-426614174222\",\n      \"userId\": \"123e4567-e89b-12d3-a456-426614174333\"\n    }\n  }\n}\n```\n\n- **eventId**: The unique identifier for the event instance.\n- **type**: The type of the event that triggered the webhook.\n- **date**: The date and time the event was triggered in ISO 8601 format.\n- **data**: Contains the `documentId`, `userId` and `accountId` relevant to the event.\n\n#### Example for `document.signed` Event\n```json\n{\n  \"event\": {\n    \"eventId\": \"123e4567-e89b-12d3-a456-426614174000\",\n    \"type\": \"document.signed\",\n    \"date\": \"2024-10-25T14:23:55Z\",\n    \"data\": {\n      \"documentId\": \"789e4567-e89b-12d3-a456-426614174111\",\n      \"accountId\": \"456e4567-e89b-12d3-a456-426614174222\",\n      \"userId\": \"123e4567-e89b-12d3-a456-426614174333\",\n      \"signeeId\": \"789e4567-e89b-12d3-a456-426614174555\"\n    }\n  }\n}\n```\n\n### Notes\n- All event payloads include `eventId`, `type`, `date`, and a `data` object with event-specific details.\n- The data object may differ between events and the `signeeId` field is only included for the `document.signed` event.\n\n### Expected Response for Webhooks\nTo acknowledge receipt of the webhook, your server should respond with an HTTP status code in the 2xx range. No specific content is required in the response body; a `200 OK` status alone will indicate successful processing.\n\n#### Example Response\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json\n```\n\n### Retry Attempts\nFormify will attempt to deliver webhook events up to 5 times if the initial attempt fails. The retry intervals* are as follows:\n- First retry: \"immediately\"\n- Second retry: after ~5 minutes\n- Third retry: after ~30 minutes \n- Fourth retry: after ~2 hours\n- Fifth retry: after ~24 hours\n\n*The intervals are not exact and may vary slightly. After 5 retries, the event will not be sent again.\n\nFormify must receive a success status code (2xx) from your server to acknowledge the event. If a success status code is not received, Formify will retry sending the event according to the intervals specified above.\n\nEach event sent by Formify includes a unique `eventId`. You can use this `eventId` to ensure that your system processes each event only once and to avoid handling duplicate events.\n\n### Security and Reliability\n\nTo secure your webhook endpoint:\n\n- **Use HTTPS**: Always use HTTPS URLs for your webhook endpoint to encrypt data in transit\n- **Verify webhook signatures**: Use HMAC-SHA256 to verify that requests are authentic (see Webhook Signature Verification below)\n- **Use eventId for idempotency**: Store processed `eventId` values to safely ignore duplicate deliveries\n\n### Webhook Delivery Headers\n\nEvery webhook delivery includes the following headers:\n\n| Header | Type | Description |\n|---|---|---|\n| `Content-Type` | string | Always `application/json` |\n| `X-Formify-Timestamp` | string | Unix timestamp (seconds) of when the event was sent |\n| `X-Formify-Signature` | string | Lowercase hexadecimal HMAC-SHA256 digest of `{timestamp}.{raw_body}` |\n\n### Webhook Signature Verification\n\nFormify signs all webhook payloads using HMAC-SHA256 so you can verify that requests are authentic. Each webhook endpoint is assigned a unique signing secret (prefixed with `whsec_`) upon creation.\n\n**How to verify:**\n\nThe signature is computed over the string `{timestamp}.{raw_body}` using your webhook's signing secret as the HMAC key.\n\n**Example (Node.js with Express):**\n```javascript\nconst crypto = require('crypto');\nconst express = require('express');\n\nconst app = express();\n\napp.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {\n  const timestamp = req.headers['x-formify-timestamp'];\n  const signature = req.headers['x-formify-signature'];\n  const secret = 'whsec_...'; // your signing secret\n\n  const signedPayload = `${timestamp}.${req.body}`;\n  const expectedSignature = crypto\n    .createHmac('sha256', secret)\n    .update(signedPayload)\n    .digest('hex');\n\n  const isValid = crypto.timingSafeEqual(\n    Buffer.from(expectedSignature),\n    Buffer.from(signature)\n  );\n\n  if (!isValid) {\n    return res.status(401).send('Invalid signature');\n  }\n\n  const event = JSON.parse(req.body);\n\n  // Process the event...\n  res.status(200).send('OK');\n});\n```\n\n**Important:** Use the raw request body exactly as received when verifying the signature. Do not verify against a re-serialized JSON payload.\n\n**Signature format:** The `X-Formify-Signature` header contains the lowercase hexadecimal HMAC-SHA256 digest of:\n\n```\n{timestamp}.{raw_body}\n```\n\n**Timestamp verification:** You should also verify that the `X-Formify-Timestamp` value is recent to reduce replay attacks.\n\n### Testing Webhooks\n\nFor local development, you can use tools such as [ngrok](https://ngrok.com) to expose your local server to the internet and receive webhook calls from Formify.\n\nExample:\n\n    ngrok http 3000\n\nThis gives you a public HTTPS URL that you can register as your webhook endpoint for testing.\n\nYou can also use request inspection tools such as:\n\n- [webhook.site](https://webhook.site) — Inspect webhook payloads without writing code\n- [requestbin.com](https://requestbin.com) — Capture and inspect HTTP requests\n\nThese tools are useful for development and debugging before you build your own webhook handler.\n\n**Typical test flow**\n1. Register a webhook endpoint\n2. Trigger an event, for example by creating or signing a document\n3. Verify that the webhook payload is received\n4. Return a `2xx` response from your server\n\n> **Note:** These tools are useful for development and debugging. Production webhook endpoints should use your own secure, publicly accessible HTTPS endpoint.\n\n### Idempotency\n\nWebhooks may be delivered more than once due to network issues or retries. To handle this:\n\n```javascript\n// Example: Node.js/Express webhook handler\napp.post('/webhook', async (req, res) => {\n  const { event } = req.body;\n  const eventId = event.eventId;\n\n  // Check if we've already processed this event\n  const alreadyProcessed = await db.checkEventId(eventId);\n  if (alreadyProcessed) {\n    console.log(`Event ${eventId} already processed, skipping`);\n    return res.status(200).send('OK');\n  }\n\n  // Store eventId to prevent duplicate processing\n  await db.storeEventId(eventId);\n\n  // Process the event\n  await handleEvent(event);\n\n  res.status(200).send('OK');\n});\n```\n\n### Debugging\n\n**Common Issues:**\n\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Webhook not received | URL incorrect or server down | Verify URL is accessible from the internet |\n| Webhook retrying repeatedly | Server not returning 2xx status | Check server logs, ensure 200/201/204 response |\n| Duplicate events | Not tracking eventId | Implement idempotency using eventId |\n| Events out of order | Network delays | Don't rely on event order; use timestamps |\n\n**Logging:**\n- Log all incoming webhook requests with timestamp, eventId, and event type\n- Log your server's response status code\n\n**Monitoring:**\n- Track webhook delivery success rate in Formify dashboard (if available)\n- Set up alerts if webhook endpoint returns errors consistently\n- Monitor processing time to ensure webhooks are handled quickly\n",
        "operationId": "registerWebhook",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "webhookUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "The URL where webhook events will be sent."
                  },
                  "name": {
                    "type": "string",
                    "description": "The name/alias of the webhook for you to identify it."
                  },
                  "eventTypes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "document.created",
                        "document.opened",
                        "document.signed",
                        "document.completed",
                        "document.revoked",
                        "document.deleted"
                      ]
                    },
                    "description": "A list of event types that the webhook will be subscribed to."
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Webhook registered successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhookId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The unique identifier of the registered webhook."
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "document.created",
                          "document.opened",
                          "document.signed",
                          "document.completed",
                          "document.revoked",
                          "document.deleted"
                        ]
                      },
                      "description": "A list of event types the webhook is subscribed to."
                    },
                    "signingSecret": {
                      "type": "string",
                      "description": "HMAC signing secret for this webhook. Store this securely — it is used to verify webhook payloads.\nPrefixed with `whsec_`.",
                      "example": "whsec_dGhpcyBpcyBhIHNhbXBsZSBzZWNyZXQ="
                    }
                  }
                },
                "example": {
                  "webhookId": "123e4567-e89b-12d3-a456-426614174000",
                  "eventTypes": [
                    "document.opened",
                    "document.signed"
                  ],
                  "signingSecret": "whsec_dGhpcyBpcyBhIHNhbXBsZSBzZWNyZXQ="
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/webhooks\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"webhookUrl\": \"string\",\n  \"name\": \"string\",\n  \"eventTypes\": [\n    \"document.created\"\n  ]\n}'"
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/webhooks\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    \"webhookUrl\": \"string\",\n    \"name\": \"string\",\n    \"eventTypes\": [\n        \"document.created\"\n    ]\n})\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/webhooks\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\npayload = {\n    \"webhookUrl\": \"string\",\n    \"name\": \"string\",\n    \"eventTypes\": [\n        \"document.created\"\n    ]\n}\n\nresponse = requests.post(url, headers=headers, json=payload)\nprint(response.json())"
          }
        ]
      },
      "get": {
        "tags": [
          "webhooks"
        ],
        "summary": "Get all registered webhooks",
        "description": "This endpoint retrieves all registered webhooks.",
        "operationId": "getAllWebhooks",
        "parameters": [
          {
            "in": "query",
            "name": "errorLanguage",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ]
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "200": {
            "description": "List of registered webhooks fetched successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "webhookId": {
                        "type": "string",
                        "format": "uuid",
                        "description": "The unique identifier of the webhook."
                      },
                      "webhookUrl": {
                        "type": "string",
                        "format": "uri",
                        "description": "The URL where webhook events are sent."
                      },
                      "name": {
                        "type": "string",
                        "description": "The name of the webhook."
                      },
                      "eventTypes": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "description": "The event types that the webhook is subscribed to."
                        }
                      },
                      "signingSecret": {
                        "type": "string",
                        "description": "HMAC signing secret for this webhook.",
                        "example": "whsec_dGhpcyBpcyBhIHNhbXBsZSBzZWNyZXQ="
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/webhooks\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/webhooks\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/webhooks\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    },
    "/webhooks/{webhookId}": {
      "get": {
        "tags": [
          "webhooks"
        ],
        "summary": "Get a specific webhook",
        "description": "This endpoint retrieves a specific webhook by its unique identifier.",
        "operationId": "getWebhookById",
        "parameters": [
          {
            "in": "path",
            "name": "webhookId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the webhook to retrieve."
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhookId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The unique identifier of the webhook."
                    },
                    "webhookUrl": {
                      "type": "string",
                      "format": "uri",
                      "description": "The URL where webhook events are sent."
                    },
                    "name": {
                      "type": "string",
                      "description": "The name of the webhook."
                    },
                    "eventTypes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "description": "The event types that the webhook is subscribed to."
                      }
                    },
                    "hasSigningSecret": {
                      "type": "boolean",
                      "description": "Whether HMAC signing secret is used for this webhook.",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Not Found - Webhook not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X GET \"https://docs-api.formify.eu/v1/webhooks/{webhookId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/webhooks/{webhookId}\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/webhooks/{webhookId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())"
          }
        ]
      },
      "delete": {
        "tags": [
          "webhooks"
        ],
        "summary": "Delete a webhook",
        "description": "This endpoint deletes a registered webhook.",
        "operationId": "deleteWebhook",
        "parameters": [
          {
            "in": "path",
            "name": "webhookId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the webhook to delete."
          }
        ],
        "responses": {
          "204": {
            "description": "Webhook deleted successfully. No content returned."
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "404": {
            "description": "Not Found - Webhook not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X DELETE \"https://docs-api.formify.eu/v1/webhooks/{webhookId}\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/webhooks/{webhookId}\", {\n  method: \"DELETE\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconsole.log(response.status); // 204"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/webhooks/{webhookId}\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.delete(url, headers=headers)\nprint(response.status_code)  # 204"
          }
        ]
      }
    },
    "/webhooks/{webhookId}/rotate-secret": {
      "post": {
        "tags": [
          "webhooks"
        ],
        "summary": "Rotate webhook signing secret",
        "description": "Generates a new signing secret for the specified webhook. The old secret is immediately invalidated. Make sure to update your verification logic with the new secret.",
        "operationId": "rotateWebhookSecret",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the webhook."
          },
          {
            "name": "errorLanguage",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "sv",
                "es"
              ],
              "default": "en"
            },
            "description": "Language for the error message"
          }
        ],
        "responses": {
          "200": {
            "description": "Secret rotated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhookId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The unique identifier of the webhook."
                    },
                    "signingSecret": {
                      "type": "string",
                      "description": "The new signing secret.",
                      "example": "whsec_bmV3IHNlY3JldCBnZW5lcmF0ZWQ="
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse401"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — webhook belongs to another account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse403"
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found or disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse404"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse500"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "source": "curl -X POST \"https://docs-api.formify.eu/v1/webhooks/{webhookId}/rotate-secret\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\""
          },
          {
            "lang": "JavaScript",
            "source": "const response = await fetch(\"https://docs-api.formify.eu/v1/webhooks/{webhookId}/rotate-secret\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n  }\n});\n\nconst data = await response.json();\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "source": "import requests\n\nurl = \"https://docs-api.formify.eu/v1/webhooks/{webhookId}/rotate-secret\"\nheaders = {\n    \"Authorization\": \"Bearer YOUR_ACCESS_TOKEN\"\n}\n\nresponse = requests.post(url, headers=headers)\nprint(response.json())"
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic Authentication for traditional OAuth clients. Send `Authorization: Basic base64(client_id:client_secret)`."
      },
      "OAuth2": {
        "type": "oauth2",
        "description": "OAuth2 Authentication. Allows third-party systems to access the API on behalf of the user and their account after authorization.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.formify.eu/oauth",
            "tokenUrl": "https://docs-api.formify.eu/v1/oauth/token",
            "scopes": {
              "read": "Allows reading documents and their metadata",
              "write": "Allows modifying documents and sending signature requests"
            }
          }
        }
      }
    },
    "schemas": {
      "SignatureField": {
        "type": "object",
        "required": [
          "fullName"
        ],
        "description": "Represents a signee who will sign the document. Each SignatureField defines one person's signature requirements, including their contact information, signature type, placement, and signing order.\n\n**Usage:**\n- Include in the `signeeDetails` array when creating a document via `POST /docs`\n- At least one SignatureField is required per document\n- Multiple SignatureFields enable multi-party signing workflows\n\n### Required vs optional fields\n\n| Field | Required? | Notes |\n|---|---|---|\n| `fullName` | **Required** | |\n| `emailAddress` | **Required unless `phoneNumber` is set** | At least one contact method must be provided |\n| `phoneNumber` | **Required unless `emailAddress` is set** | At least one contact method must be provided |\n| `phoneNumberDeliveryMethod` | Optional | Defaults to `sms` when `phoneNumber` is set |\n| `signatureType` | Optional | Defaults to `digital_ink` |\n| `signaturePlacement` | Optional | Defaults to `existing` — signature placed on an existing page. Use `new_page` to append a dedicated signature page instead. When `existing`, `signatureBox` coordinates are required |\n| `signatureBox` | Conditional | Required when `signaturePlacement` is `existing` (or omitted, since `existing` is the default) |\n| `idScanBox` | Conditional | Required when `signatureType` is `digital_ink_id_scan` |\n| `signingOrder` | Optional | Defaults to `1`. Only relevant when `enableSigningOrder` is `true` on the document |\n\n**Special Rules:**\n- When `signaturePlacement` is `existing`, `signatureBox.page`, `signatureBox.x`, and `signatureBox.y` are required.\n- When `signatureType` is `digital_ink_id_scan`, `signaturePlacement` must be `existing` and both `signatureBox` and `idScanBox` are required.\n\n**Common Scenarios:**\n- Single signee: One SignatureField with default settings\n- Multiple simultaneous signees: Multiple SignatureFields with same `signingOrder` value\n- Sequential signing: Multiple SignatureFields with incrementing `signingOrder` values (1, 2, 3...)",
        "example": {
          "fullName": "Jane Smith",
          "emailAddress": "jane.smith@example.com",
          "phoneNumber": "+46701234567",
          "phoneNumberDeliveryMethod": "sms",
          "signatureType": "digital_ink_id_scan",
          "signaturePlacement": "existing",
          "signatureBox": {
            "page": 0,
            "x": 72,
            "y": 560,
            "scale": 1
          },
          "idScanBox": {
            "page": 0,
            "x": 320,
            "y": 480,
            "scale": 1
          },
          "signingOrder": 1
        },
        "properties": {
          "fullName": {
            "type": "string",
            "description": "Full name of the signee"
          },
          "emailAddress": {
            "type": "string",
            "format": "email",
            "description": "Email address of the signee"
          },
          "phoneNumber": {
            "type": "string",
            "description": "The signee's phone number, formatted as an international number with a leading `+` followed by 9 to 15 digits.\n\n### Format\n- Must start with a `+`, followed by the country code and phone number.\n- Avoid spaces, dashes, or other separators.\n\nRequires `deliverySms` or `deliveryWhatsapp` capability. Check `GET /account/capabilities`.",
            "pattern": "^\\+[0-9]{9,15}$"
          },
          "phoneNumberDeliveryMethod": {
            "type": "string",
            "enum": [
              "sms",
              "whatsapp"
            ],
            "default": "sms",
            "description": "Method used to deliver messages to the provided `phoneNumber`.\n\n- `sms`: Send an SMS mobile phone message (default, requires `deliverySms` capability)\n- `whatsapp`: Send via WhatsApp (requires `deliveryWhatsapp` capability)\n\nOnly applicable when `phoneNumber` is set."
          },
          "signatureType": {
            "type": "string",
            "enum": [
              "digital_ink",
              "bankid_identification",
              "digital_ink_id_scan",
              "face_liveness"
            ],
            "default": "digital_ink",
            "description": "Type of signature / identity verification method used by the signee.\n\n- `digital_ink`: The signee draws their signature using a mouse, touchpad, or finger.\n- `bankid_identification`: The signee signs using Swedish BankID (requires `signatureBankId` capability).\n- `digital_ink_id_scan`: The signee verifies identity with an ID scan and then signs with digital ink (requires `signatureIdScan` capability).\n- `face_liveness`: The signee verifies identity using an ID scan together with a video selfie biometric liveness check (requires `signatureFaceLiveness` capability).\n\nCheck `GET /account/capabilities` to see which signature types are enabled."
          },
          "signaturePlacement": {
            "type": "string",
            "enum": [
              "existing",
              "new_page"
            ],
            "default": "existing",
            "description": "Option to place the signature box on an existing page (`existing`, default) or place the field on a new signature page (A4 portrait format) at the end of the document (`new_page`).\n\n`digital_ink_id_scan` does not support `new_page` and must use `existing`, because both the signature field and the ID scan placeholder must be placed inside the document."
          },
          "signingOrder": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "Defines the order in which this signee can sign the document. Only applicable when `enableSigningOrder` is set to `true` in the document request.\n\n- **Default**: `1` (all signees can sign simultaneously)\n- **Sequential Signing**: Set different values (1, 2, 3, etc.) to enforce a signing sequence\n- **Simultaneous Signing**: Multiple signees with the same value can sign at the same time\n\nSignees will receive their invitation (email/SMS/WhatsApp) only when it's their turn to sign based on this order."
          },
          "signatureBox": {
            "type": "object",
            "description": "Placement of the signature field when `signaturePlacement` is `existing`.\n\nBase size before scaling:\n- Width: 219 points\n- Height: 58 points",
            "properties": {
              "page": {
                "type": "integer",
                "description": "Page number where the signature box should be placed. The first page has index 0."
              },
              "x": {
                "type": "integer",
                "minimum": 0,
                "description": "X-coordinate for the signature box placement (minimum value 0)"
              },
              "y": {
                "type": "integer",
                "minimum": 0,
                "description": "Y-coordinate for the signature box placement (minimum value 0)"
              },
              "scale": {
                "type": "number",
                "format": "float",
                "minimum": 0.25,
                "maximum": 1.5,
                "default": 1,
                "description": "Scales the entire signature field. Applies only when `signaturePlacement` is `existing`."
              }
            }
          },
          "idScanBox": {
            "type": "object",
            "description": "Placement of the scanned ID placeholder used when `signatureType` is `digital_ink_id_scan`.\n\nThis property is required for `digital_ink_id_scan` and is not used for other signature types.\n\nBase size before scaling:\n- Width: 218 points\n- Height: 138 points",
            "properties": {
              "page": {
                "type": "integer",
                "description": "Page number where the ID scan placeholder should be placed. The first page has index 0."
              },
              "x": {
                "type": "integer",
                "minimum": 0,
                "description": "X-coordinate for the ID scan placeholder placement (minimum value 0)"
              },
              "y": {
                "type": "integer",
                "minimum": 0,
                "description": "Y-coordinate for the ID scan placeholder placement (minimum value 0)"
              },
              "scale": {
                "type": "number",
                "format": "float",
                "minimum": 0.25,
                "maximum": 1.5,
                "default": 1,
                "description": "Scales the entire ID scan placeholder. Applies only when `signatureType` is `digital_ink_id_scan`."
              }
            }
          }
        }
      },
      "DraftSignatureField": {
        "type": "object",
        "description": "Represents a signee in a draft document. This schema shares the same properties as `SignatureField` but with no required fields — all properties are optional during the draft phase.\n\n**Draft-specific behavior:**\n- **All fields are optional** when creating or updating a draft (but just as for regular document signature requests, the required fields must be entered before sending the draft).\n- Contact methods (`emailAddress`, `phoneNumber`) are only validated when the draft is sent via `POST /docs/drafts/{draftId}/send`.\n- `signatureBox` coordinates can be omitted or incomplete — they are only validated at send time.\n- `idScanBox` coordinates for `digital_ink_id_scan` can also be omitted until the draft is sent.\n\nWhen the draft is sent, all the same validations as `POST /docs` apply (`fullName` required, at least one contact method, valid coordinates, etc.).",
        "example": {
          "fullName": "Jane Smith",
          "emailAddress": "jane.smith@example.com",
          "signatureType": "digital_ink",
          "signaturePlacement": "existing",
          "signatureBox": {
            "page": 0,
            "x": 72,
            "y": 560,
            "scale": 1
          },
          "signingOrder": 1
        },
        "properties": {
          "fullName": {
            "type": "string",
            "description": "Full name of the signee"
          },
          "emailAddress": {
            "type": "string",
            "format": "email",
            "description": "Email address of the signee. Optional during draft phase; required when sending the draft (unless `phoneNumber` is set)."
          },
          "phoneNumber": {
            "type": "string",
            "description": "The signee's phone number, formatted as an international number with a leading `+` followed by 9 to 15 digits. Optional during draft phase; required when sending the draft (unless `emailAddress` is set).\n\nRequires `deliverySms` or `deliveryWhatsapp` capability. Check `GET /account/capabilities`.",
            "pattern": "^\\+[0-9]{9,15}$"
          },
          "phoneNumberDeliveryMethod": {
            "type": "string",
            "enum": [
              "sms",
              "whatsapp"
            ],
            "default": "sms",
            "description": "Method used to deliver messages to the provided `phoneNumber`.\n\n- `sms`: Send an SMS mobile phone message (default, requires `deliverySms` capability)\n- `whatsapp`: Send via WhatsApp (requires `deliveryWhatsapp` capability)\n\nOnly applicable when `phoneNumber` is set."
          },
          "signatureType": {
            "type": "string",
            "enum": [
              "digital_ink",
              "bankid_identification",
              "digital_ink_id_scan",
              "face_liveness"
            ],
            "default": "digital_ink",
            "description": "Type of signature / identity verification method used by the signee.\n\n- `digital_ink`: The signee draws their signature using a mouse, touchpad, or finger.\n- `bankid_identification`: The signee signs using Swedish BankID (requires `signatureBankId` capability).\n- `digital_ink_id_scan`: The signee verifies identity with an ID scan and then signs with digital ink (requires `signatureIdScan` capability).\n- `face_liveness`: The signee verifies identity using an ID scan together with a video selfie biometric liveness check (requires `signatureFaceLiveness` capability).\n\nCheck `GET /account/capabilities` to see which signature types are enabled."
          },
          "signaturePlacement": {
            "type": "string",
            "enum": [
              "existing",
              "new_page"
            ],
            "default": "existing",
            "description": "Option to place the signature box on an existing page (`existing`, default) or place the field on a new signature page (A4 portrait format) at the end of the document (`new_page`).\n\n`digital_ink_id_scan` does not support `new_page` and must use `existing`, because both the signature field and the ID scan placeholder must be placed inside the document."
          },
          "signingOrder": {
            "type": "integer",
            "minimum": 1,
            "default": 1,
            "description": "Defines the order in which this signee can sign the document. Only applicable when `enableSigningOrder` is set to `true` on the draft.\n\n- **Default**: `1` (all signees can sign simultaneously)\n- **Sequential Signing**: Set different values (1, 2, 3, etc.) to enforce a signing sequence\n- **Simultaneous Signing**: Multiple signees with the same value can sign at the same time"
          },
          "signatureBox": {
            "type": "object",
            "description": "Placement of the signature field when `signaturePlacement` is `existing`. Optional during draft phase; validated when the draft is sent.\n\nBase size before scaling:\n- Width: 219 points\n- Height: 58 points",
            "properties": {
              "page": {
                "type": "integer",
                "description": "Page number where the signature box should be placed. The first page has index 0."
              },
              "x": {
                "type": "integer",
                "minimum": 0,
                "description": "X-coordinate for the signature box placement (minimum value 0)"
              },
              "y": {
                "type": "integer",
                "minimum": 0,
                "description": "Y-coordinate for the signature box placement (minimum value 0)"
              },
              "scale": {
                "type": "number",
                "format": "float",
                "minimum": 0.25,
                "maximum": 1.5,
                "default": 1,
                "description": "Scales the entire signature field. Applies only when `signaturePlacement` is `existing`."
              }
            }
          },
          "idScanBox": {
            "type": "object",
            "description": "Placement of the scanned ID placeholder used when `signatureType` is `digital_ink_id_scan`. Optional during draft phase; validated when the draft is sent.\n\nBase size before scaling:\n- Width: 218 points\n- Height: 138 points",
            "properties": {
              "page": {
                "type": "integer",
                "description": "Page number where the ID scan placeholder should be placed. The first page has index 0."
              },
              "x": {
                "type": "integer",
                "minimum": 0,
                "description": "X-coordinate for the ID scan placeholder placement (minimum value 0)"
              },
              "y": {
                "type": "integer",
                "minimum": 0,
                "description": "Y-coordinate for the ID scan placeholder placement (minimum value 0)"
              },
              "scale": {
                "type": "number",
                "format": "float",
                "minimum": 0.25,
                "maximum": 1.5,
                "default": 1,
                "description": "Scales the entire ID scan placeholder. Applies only when `signatureType` is `digital_ink_id_scan`."
              }
            }
          }
        }
      },
      "AiAssistantResponse": {
        "type": "object",
        "nullable": true,
        "description": "AI Assistant configuration for this document. Only included in the response if the document was created with AI Assistant enabled.\n",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether AI Assistant is enabled for this document.",
            "example": true
          },
          "textToSpeech": {
            "type": "boolean",
            "description": "Whether text-to-speech is enabled for the AI Assistant.",
            "example": false
          },
          "language": {
            "type": "string",
            "description": "The language configured for the AI Assistant.",
            "example": "en",
            "enum": [
              "ar",
              "bg",
              "zh",
              "hr",
              "cs",
              "da",
              "nl",
              "en",
              "fil",
              "fi",
              "fr",
              "de",
              "el",
              "hi",
              "id",
              "it",
              "ja",
              "ko",
              "ms",
              "pl",
              "pt",
              "ro",
              "ru",
              "es",
              "sv",
              "ta",
              "tr",
              "uk"
            ]
          }
        }
      },
      "FormField": {
        "type": "object",
        "description": "Represents an interactive form field embedded in a PDF document. FormFields are extracted from PDF forms and can be read or updated via the API.\n\n**Usage:**\n- Retrieve form fields from uploaded files: `GET /files/{fileId}/fields`\n- Retrieve form fields from documents: `GET /docs/{documentId}/fields`\n- Get field values from completed documents: `GET /docs/{documentId}/field-values`\n- Update field values: `POST /docs/{documentId}/field-values`\n\n**Field Types:**\n- **textField**: Single-line or multi-line text input\n- **checkbox**: Boolean selection (checked/unchecked)\n- **radioButton**: Single selection from a group of options\n- **combobox**: Dropdown with editable text input\n- **listbox**: List selection (single or multiple)\n\n**Common Use Cases:**\n- Pre-fill contract templates with customer data\n- Collect structured information from signees\n- Create dynamic forms with conditional logic\n- Extract data from completed forms for processing\n\n**Key Properties:**\n- `name`: Unique identifier for the field (use this when updating values)\n- `required`: Whether the field must be filled before signing\n- `editable`: Whether the field can be updated via API\n- `readOnly`: Whether signees can modify the field",
        "example": {
          "fieldId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
          "name": "customer_email",
          "label": "Email Address",
          "type": "textField",
          "value": null,
          "required": true,
          "readOnly": false,
          "editable": true,
          "page": 0
        },
        "properties": {
          "fieldId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the field."
          },
          "name": {
            "type": "string",
            "description": "The name of the form field, used as the key for API interactions."
          },
          "label": {
            "type": "string",
            "description": "The display label of the form field."
          },
          "type": {
            "type": "string",
            "enum": [
              "checkbox",
              "combobox",
              "listbox",
              "radioButton",
              "textField"
            ],
            "description": "The type of the form field."
          },
          "value": {
            "type": "string",
            "nullable": true,
            "description": "The initial value of the form field, if any."
          },
          "defaultValues": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "List of default values for the form field."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string",
                  "description": "The display label of the option."
                },
                "value": {
                  "type": "string",
                  "description": "The value associated with the option."
                }
              }
            },
            "nullable": true,
            "description": "List of available options for dropdown or choice fields."
          },
          "required": {
            "type": "boolean",
            "description": "Indicates if the field is mandatory for submission."
          },
          "readOnly": {
            "type": "boolean",
            "description": "Indicates if the field is read-only and cannot be modified."
          },
          "editable": {
            "type": "boolean",
            "description": "Indicates if the field is editable through the `POST /docs/{documentId}/field-values` endpoint."
          },
          "multiLine": {
            "type": "boolean",
            "nullable": true,
            "description": "Indicates if the field supports multi-line text."
          },
          "comb": {
            "type": "boolean",
            "nullable": true,
            "description": "Indicates if the field uses comb formatting for text input."
          },
          "page": {
            "type": "integer",
            "description": "The page index (zero-based) where the field is located."
          },
          "boundingBox": {
            "type": "object",
            "nullable": true,
            "properties": {
              "x": {
                "type": "number",
                "description": "The X coordinate of the field's top-left corner."
              },
              "y": {
                "type": "number",
                "description": "The Y coordinate of the field's top-left corner."
              },
              "width": {
                "type": "number",
                "description": "The width of the field in points."
              },
              "height": {
                "type": "number",
                "description": "The height of the field in points."
              }
            },
            "description": "The bounding box of the field on the page."
          }
        }
      },
      "FormFieldValue": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The name of the form field to update."
          },
          "value": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ],
            "description": "The new value(s) for the form field.\n- **For single-value fields (e.g., text fields, radio buttons):** Provide a **single string**.\n- **For multi-value fields (e.g., checkboxes, listboxes):** Provide an **array of strings**.",
            "example": "John Doe"
          }
        },
        "example": {
          "name": "email",
          "value": "test@example.com"
        }
      },
      "AiAssistant": {
        "type": "object",
        "description": "Configuration for the optional AI Assistant shown to signees while reviewing and signing a document.\n\n**Usage:**\n- Set `enabled` to `true` to activate the assistant for the document.\n- Set `textToSpeech` to `true` if the assistant should be able to read its responses aloud.\n- Use `language` to control the assistant language.\n\n**Defaults:**\n- `enabled`: `false` \n- `textToSpeech`: `false` \n- `language`: `en` \n\n**Processing:**\nWhen the AI Assistant is enabled, the document will be created with `status: \"processing\"` and `documentUrl` will be `null`. The document will be finalized asynchronously, and invitations to signees will be sent once processing is complete.\n\n> **Availability:** Requires the `aiAssistant` capability. Check `GET /account/capabilities`.",
        "example": {
          "enabled": true,
          "textToSpeech": false,
          "language": "en"
        },
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": false,
            "description": "Enables the AI Assistant for the document."
          },
          "textToSpeech": {
            "type": "boolean",
            "default": false,
            "description": "Enables text-to-speech so the assistant can read its responses aloud."
          },
          "language": {
            "type": "string",
            "enum": [
              "ar",
              "bg",
              "zh",
              "hr",
              "cs",
              "da",
              "nl",
              "en",
              "fil",
              "fi",
              "fr",
              "de",
              "el",
              "hi",
              "id",
              "it",
              "ja",
              "ko",
              "ms",
              "pl",
              "pt",
              "ro",
              "ru",
              "es",
              "sv",
              "ta",
              "tr",
              "uk"
            ],
            "default": "en",
            "description": "Language used by the AI Assistant. Supports 28 languages."
          }
        }
      },
      "OauthTokenResponse": {
        "type": "object",
        "description": "OAuth 2.0 token response returned after successful authorization. This object contains the access token (JWT) and refresh token needed to authenticate API requests.\n\n**Usage:**\n- Include `access_token` in the `Authorization: Bearer {token}` header for all API requests\n- Use `refresh_token` to obtain a new access token before expiration\n- Store tokens securely (encrypted database or secure session storage)",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "The JWT (JSON Web Token) access token used for authenticating API requests. This token must be included in the `Authorization` header as `Bearer {access_token}` for all protected endpoints.\n\n**Format:** JWT with three parts separated by dots (header.payload.signature)\n**Lifetime:** Specified in `expires_in` field (typically 3600 seconds / 1 hour)",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
          },
          "expires_in": {
            "type": "integer",
            "description": "The lifetime of the access token in seconds. After this time, the token expires and can no longer be used.\n\n**Typical value:** 3600 (1 hour)\n**Best practice:** Refresh the token proactively before expiration to avoid service interruption",
            "example": 3600
          },
          "token_type": {
            "type": "string",
            "description": "The type of token issued. Always `Bearer` for OAuth 2.0 access tokens.\n\n**Usage:** Include as `Authorization: Bearer {access_token}` in request headers",
            "example": "Bearer"
          },
          "refresh_token": {
            "type": "string",
            "description": "A long-lived token used to obtain a new access token when the current one expires. Unlike access tokens, refresh tokens do not expire automatically but can be revoked.\n\n**Usage:** Send to `/oauth/token` with `grant_type=refresh_token` to get a new access token\n**Security:** Store securely and never expose in client-side code or logs",
            "example": "def502004a1b2c3d4e5f6789abcdef0123456789"
          },
          "scope": {
            "type": "string",
            "description": "Space-separated list of permissions granted to this token. Determines which API operations the token can access.\n\n**Possible values:**\n- `read`: Read-only access to documents, files, and account data\n- `write`: Full access including creating/updating documents and files\n- `read write`: Both read and write permissions",
            "example": "read write"
          }
        }
      },
      "ErrorResponse401": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string",
            "example": "Unauthorized"
          },
          "code": {
            "type": "integer",
            "example": 401
          },
          "language": {
            "type": "string",
            "example": "en"
          }
        }
      },
      "ErrorResponse403": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string",
            "example": "Forbidden"
          },
          "code": {
            "type": "integer",
            "example": 403
          },
          "language": {
            "type": "string",
            "example": "en"
          }
        }
      },
      "RateLimitHeaders": {
        "type": "object",
        "properties": {
          "X-RateLimit-Limit": {
            "type": "integer",
            "example": 50,
            "description": "The maximum number of requests allowed within a replenishment period."
          },
          "X-RateLimit-Remaining": {
            "type": "integer",
            "example": 46,
            "description": "The number of requests remaining in the current replenishment period."
          },
          "X-RateLimit-Reset": {
            "type": "integer",
            "example": 2,
            "description": "The number of seconds until the rate limit resets."
          }
        }
      },
      "ErrorResponse404": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string",
            "example": "Not Found"
          },
          "code": {
            "type": "integer",
            "example": 404
          },
          "language": {
            "type": "string",
            "example": "en"
          }
        }
      },
      "ErrorResponse500": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string",
            "example": "Internal Server Error"
          },
          "code": {
            "type": "integer",
            "example": 500
          },
          "language": {
            "type": "string",
            "example": "en"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string",
            "example": "Invalid request parameters"
          },
          "code": {
            "type": "integer",
            "example": 400
          },
          "language": {
            "type": "string",
            "example": "en"
          }
        }
      },
      "AccountCapabilities": {
        "type": "object",
        "description": "Describes which features and add-ons are enabled for the account.\n\nUse this endpoint to discover what capabilities are available before attempting to use them. Features that are not enabled will result in a `403 Forbidden` response if used.",
        "properties": {
          "deliverySms": {
            "type": "boolean",
            "description": "Whether SMS delivery is enabled for signee invitations and reminders.",
            "example": true
          },
          "deliveryWhatsapp": {
            "type": "boolean",
            "description": "Whether WhatsApp delivery is enabled for signee invitations and reminders.",
            "example": false
          },
          "templates": {
            "type": "boolean",
            "description": "Whether templates can be used (e.g. listed and applied to documents).",
            "example": true
          },
          "templatesCreate": {
            "type": "boolean",
            "description": "Whether new templates can be created.",
            "example": true
          },
          "signingOrder": {
            "type": "boolean",
            "description": "Whether sequential signing order is enabled for multi-party documents.",
            "example": true
          },
          "signAndPay": {
            "type": "boolean",
            "description": "Whether the Sign & Pay feature is enabled.",
            "example": false
          },
          "signAndPayCreate": {
            "type": "boolean",
            "description": "Whether Sign & Pay links can be created.",
            "example": false
          },
          "signatureBankId": {
            "type": "boolean",
            "description": "Whether the `bankid_identification` signature type is enabled.",
            "example": false
          },
          "signatureIdScan": {
            "type": "boolean",
            "description": "Whether the `digital_ink_id_scan` signature type is enabled.",
            "example": true
          },
          "signatureFaceLiveness": {
            "type": "boolean",
            "description": "Whether the `face_liveness` signature type is enabled.",
            "example": false
          },
          "verificationToOpen": {
            "type": "boolean",
            "description": "Whether the verification-to-open setting can be enabled on documents.",
            "example": false
          },
          "disableSubmit": {
            "type": "boolean",
            "description": "Whether the disable submit/sign setting can be enabled on documents.",
            "example": false
          },
          "searchableFormFields": {
            "type": "boolean",
            "description": "Whether searchable form fields are enabled.",
            "example": false
          },
          "apiAccess": {
            "type": "boolean",
            "description": "Whether API integration settings are accessible.",
            "example": true
          },
          "companyLogo": {
            "type": "boolean",
            "description": "Whether a custom company logo can be configured.",
            "example": true
          },
          "sharedDocuments": {
            "type": "boolean",
            "description": "Whether shared document settings within the account are enabled.",
            "example": false
          },
          "verificationEmail": {
            "type": "boolean",
            "description": "Whether the Email Address Verification add-on is enabled.",
            "example": false
          },
          "verificationPhone": {
            "type": "boolean",
            "description": "Whether the Mobile Number Verification add-on is enabled.",
            "example": false
          },
          "initials": {
            "type": "boolean",
            "description": "Whether the Initials add-on is enabled.",
            "example": false
          },
          "aiAssistant": {
            "type": "boolean",
            "description": "Whether the AI Assistant feature is enabled for the account.",
            "example": false
          },
          "aiAssistantCustomProfile": {
            "type": "boolean",
            "description": "Whether a custom AI assistant profile can be configured.",
            "example": false
          }
        },
        "example": {
          "deliverySms": true,
          "deliveryWhatsapp": false,
          "templates": true,
          "templatesCreate": true,
          "signingOrder": true,
          "signAndPay": false,
          "signAndPayCreate": false,
          "signatureBankId": false,
          "signatureIdScan": true,
          "signatureFaceLiveness": false,
          "verificationToOpen": false,
          "disableSubmit": false,
          "searchableFormFields": false,
          "apiAccess": true,
          "companyLogo": true,
          "sharedDocuments": false,
          "verificationEmail": false,
          "verificationPhone": false,
          "initials": false,
          "aiAssistant": false,
          "aiAssistantCustomProfile": false
        }
      },
      "CreateDraftRequest": {
        "type": "object",
        "required": [
          "fileId"
        ],
        "properties": {
          "fileId": {
            "type": "string",
            "format": "uuid",
            "description": "File ID from `POST /files`"
          },
          "name": {
            "type": "string",
            "description": "Name of the draft"
          },
          "signeeDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DraftSignatureField"
            },
            "description": "List of signees. Contact methods and placement coordinates are optional during the draft phase."
          },
          "enableSigningOrder": {
            "type": "boolean",
            "default": false,
            "description": "Enable sequential signing order. Requires `signingOrder` capability."
          },
          "language": {
            "type": "string",
            "enum": [
              "en",
              "sv",
              "es"
            ],
            "default": "en",
            "description": "Language of the document"
          },
          "sharingSetting": {
            "type": "string",
            "enum": [
              "private",
              "shared"
            ],
            "description": "Sharing setting"
          },
          "userId": {
            "type": "string",
            "format": "uuid",
            "description": "Assign ownership to a different user within the same account"
          },
          "personalMessage": {
            "type": "string",
            "maxLength": 500,
            "description": "Message included in invitations when the draft is sent. Max 500 characters."
          },
          "aiAssistant": {
            "$ref": "#/components/schemas/AiAssistant"
          },
          "errorLanguage": {
            "type": "string",
            "enum": [
              "en",
              "sv",
              "es"
            ],
            "description": "Language for error messages"
          }
        }
      },
      "UpdateDraftRequest": {
        "type": "object",
        "properties": {
          "fileId": {
            "type": "string",
            "format": "uuid",
            "description": "File ID from `POST /files`. If omitted, the draft keeps its current file."
          },
          "name": {
            "type": "string",
            "description": "Name of the draft"
          },
          "signeeDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DraftSignatureField"
            },
            "description": "List of signees. Replaces existing signees entirely. Contact methods and placement coordinates are optional during the draft phase."
          },
          "enableSigningOrder": {
            "type": "boolean",
            "default": false,
            "description": "Enable sequential signing order. Requires `signingOrder` capability."
          },
          "language": {
            "type": "string",
            "enum": [
              "en",
              "sv",
              "es"
            ],
            "default": "en",
            "description": "Language of the document"
          },
          "sharingSetting": {
            "type": "string",
            "enum": [
              "private",
              "shared"
            ],
            "description": "Sharing setting"
          },
          "userId": {
            "type": "string",
            "format": "uuid",
            "description": "Assign ownership to a different user within the same account"
          },
          "personalMessage": {
            "type": "string",
            "maxLength": 500,
            "description": "Message included in invitations when the draft is sent. Max 500 characters."
          },
          "aiAssistant": {
            "$ref": "#/components/schemas/AiAssistant"
          },
          "errorLanguage": {
            "type": "string",
            "enum": [
              "en",
              "sv",
              "es"
            ],
            "description": "Language for error messages"
          }
        }
      },
      "DraftCreatedResponse": {
        "type": "object",
        "properties": {
          "draftId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier of the created draft"
          },
          "name": {
            "type": "string",
            "description": "Name of the draft"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Draft creation timestamp"
          },
          "userId": {
            "type": "string",
            "format": "uuid",
            "description": "User ID of the draft owner"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft"
            ],
            "description": "Always `draft`"
          }
        }
      },
      "DraftListResponse": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "description": "The maximum number of drafts requested per page"
          },
          "offset": {
            "type": "integer",
            "description": "The offset used in the current request"
          },
          "nextOffset": {
            "type": "integer",
            "nullable": true,
            "description": "The next offset for pagination, or null if no more results"
          },
          "drafts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DraftListDetail"
            }
          },
          "errorLanguage": {
            "type": "string",
            "enum": [
              "en",
              "sv",
              "es"
            ],
            "description": "Language used for errors"
          }
        }
      },
      "DraftListDetail": {
        "type": "object",
        "properties": {
          "draftId": {
            "type": "string",
            "format": "uuid",
            "description": "Draft ID"
          },
          "name": {
            "type": "string",
            "description": "Name of the draft"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Draft creation timestamp"
          },
          "sharingSetting": {
            "type": "string",
            "enum": [
              "private",
              "shared"
            ],
            "description": "Sharing setting"
          },
          "userId": {
            "type": "string",
            "format": "uuid",
            "description": "User ID of the draft owner"
          }
        }
      },
      "DraftDetailsResponse": {
        "type": "object",
        "properties": {
          "draftId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier of the draft"
          },
          "name": {
            "type": "string",
            "description": "Name of the draft"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Draft creation timestamp"
          },
          "sharingSetting": {
            "type": "string",
            "enum": [
              "private",
              "shared"
            ],
            "description": "Sharing setting"
          },
          "language": {
            "type": "string",
            "enum": [
              "en",
              "sv",
              "es"
            ],
            "description": "Language of the draft"
          },
          "userId": {
            "type": "string",
            "format": "uuid",
            "description": "User ID of the draft owner"
          },
          "fileId": {
            "type": "string",
            "format": "uuid",
            "description": "File ID used for the draft"
          },
          "enableSigningOrder": {
            "type": "boolean",
            "description": "Whether signing order is enabled"
          },
          "signeeDetails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DraftSignatureField"
            }
          },
          "aiAssistant": {
            "$ref": "#/components/schemas/AiAssistantResponse"
          }
        }
      },
      "DraftSentResponse": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier of the now-live document (same ID as the draft)"
          },
          "name": {
            "type": "string",
            "description": "Name of the document"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Document creation timestamp"
          },
          "userId": {
            "type": "string",
            "format": "uuid",
            "description": "User ID of the document owner"
          },
          "documentUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Link to the document overview page in Formify. This field will be `null` when `status` is `processing` (e.g., when AI Assistant is enabled)."
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "processing"
            ],
            "description": "Status of the document"
          }
        }
      },
      "DocumentWebhookEvent": {
        "type": "object",
        "description": "The payload structure sent to your webhook endpoint when document events occur. This is the complete object your webhook receiver will receive in the request body.\n\n**Usage:**\n- Received as POST request body at your registered webhook URL\n- Content-Type: `application/json`\n- Must respond with 2xx status code to acknowledge receipt\n\n**Structure:**\n- `event`: Contains metadata about the event (eventId, type, date)\n- `data`: Contains identifiers for the affected resources (documentId, accountId, userId, signeeId)\n\n**Important Notes:**\n- Use `event.eventId` for idempotency (prevent duplicate processing)\n- The `data.signeeId` field is only present for `document.signed` events\n- Events may arrive out of order - use timestamps for sequencing\n- Store eventId to detect and ignore duplicate deliveries\n\n**Common Integration Pattern:**\n1. Receive webhook POST request\n2. Validate eventId hasn't been processed before\n3. Store eventId in database\n4. Process the event based on `event.type`\n5. Respond with 200 OK",
        "example": {
          "event": {
            "eventId": "123e4567-e89b-12d3-a456-426614174000",
            "type": "document.completed",
            "date": "2024-03-17T14:23:55Z"
          },
          "data": {
            "documentId": "789e4567-e89b-12d3-a456-426614174111",
            "accountId": "456e4567-e89b-12d3-a456-426614174222",
            "userId": "123e4567-e89b-12d3-a456-426614174333"
          }
        },
        "properties": {
          "event": {
            "type": "object",
            "properties": {
              "eventId": {
                "type": "string",
                "format": "uuid",
                "description": "Unique identifier for this webhook event."
              },
              "type": {
                "type": "string",
                "enum": [
                  "document.created",
                  "document.opened",
                  "document.signed",
                  "document.completed",
                  "document.revoked",
                  "document.deleted"
                ],
                "description": "The type of event that triggered the webhook."
              },
              "date": {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp of when the event occurred."
              }
            }
          },
          "data": {
            "type": "object",
            "description": "Event-specific data.",
            "properties": {
              "documentId": {
                "type": "string",
                "format": "uuid"
              },
              "accountId": {
                "type": "string",
                "format": "uuid"
              },
              "userId": {
                "type": "string",
                "format": "uuid"
              },
              "signeeId": {
                "type": "string",
                "format": "uuid",
                "nullable": true,
                "description": "Only present for `document.signed` events."
              }
            }
          }
        }
      }
    }
  },
  "x-tagGroups": [
    {
      "name": "Authentication",
      "tags": [
        "oauth"
      ]
    },
    {
      "name": "Documents",
      "tags": [
        "files",
        "docs",
        "drafts",
        "templates"
      ]
    },
    {
      "name": "Account & User",
      "tags": [
        "user",
        "account"
      ]
    },
    {
      "name": "Webhooks",
      "tags": [
        "webhooks"
      ]
    }
  ]
}