# Delete Action Source: https://docs.cradl.ai/api-reference/delete-actions /api-reference/openapi.json delete /actions/{actionId} Delete Action # Delete Agent Source: https://docs.cradl.ai/api-reference/delete-agents /api-reference/openapi.json delete /agents/{agentId} Delete Agent # Delete Agent Run Source: https://docs.cradl.ai/api-reference/delete-agents-runs /api-reference/openapi.json delete /agents/{agentId}/runs/{runId} Delete Agent Run # Delete Appclient Source: https://docs.cradl.ai/api-reference/delete-appclients /api-reference/openapi.json delete /appClients/{appClientId} Delete Appclient # Delete Document Source: https://docs.cradl.ai/api-reference/delete-documents /api-reference/openapi.json delete /documents Delete Document # Delete Document Source: https://docs.cradl.ai/api-reference/delete-documents-1 /api-reference/openapi.json delete /documents/{documentId} Delete Document # Delete Function Source: https://docs.cradl.ai/api-reference/delete-functions /api-reference/openapi.json delete /functions/{functionId} Delete Function # Delete Hook Source: https://docs.cradl.ai/api-reference/delete-hooks /api-reference/openapi.json delete /hooks/{hookId} Delete Hook # Delete Model Source: https://docs.cradl.ai/api-reference/delete-models /api-reference/openapi.json delete /models/{modelId} Delete Model # Delete Role Source: https://docs.cradl.ai/api-reference/delete-roles /api-reference/openapi.json delete /roles/{roleId} Delete Role # Delete User Source: https://docs.cradl.ai/api-reference/delete-users /api-reference/openapi.json delete /users/{userId} Delete User # Delete Validation Source: https://docs.cradl.ai/api-reference/delete-validations /api-reference/openapi.json delete /validations/{validationId} Delete Validation # List Actions Source: https://docs.cradl.ai/api-reference/get-actions /api-reference/openapi.json get /actions List Actions # Get Action Source: https://docs.cradl.ai/api-reference/get-actions-1 /api-reference/openapi.json get /actions/{actionId} Get Action # List Action Runs Source: https://docs.cradl.ai/api-reference/get-actions-runs /api-reference/openapi.json get /actions/{actionId}/runs List Action Runs # Get Action Run Source: https://docs.cradl.ai/api-reference/get-actions-runs-1 /api-reference/openapi.json get /actions/{actionId}/runs/{runId} Get Action Run # List Agents Source: https://docs.cradl.ai/api-reference/get-agents /api-reference/openapi.json get /agents List Agents # Get Agent Source: https://docs.cradl.ai/api-reference/get-agents-1 /api-reference/openapi.json get /agents/{agentId} Get Agent # List Agent Runs Source: https://docs.cradl.ai/api-reference/get-agents-runs /api-reference/openapi.json get /agents/{agentId}/runs List Agent Runs # Get Agent Run Source: https://docs.cradl.ai/api-reference/get-agents-runs-1 /api-reference/openapi.json get /agents/{agentId}/runs/{runId} Get Agent Run # List Agent Statistics Source: https://docs.cradl.ai/api-reference/get-agents-statistics /api-reference/openapi.json get /agents/{agentId}/statistics List Agent Statistics # List Appclients Source: https://docs.cradl.ai/api-reference/get-appclients /api-reference/openapi.json get /appClients List Appclients # Get Appclient Source: https://docs.cradl.ai/api-reference/get-appclients-1 /api-reference/openapi.json get /appClients/{appClientId} Get Appclient # List Documents Source: https://docs.cradl.ai/api-reference/get-documents /api-reference/openapi.json get /documents List Documents # Get Document Source: https://docs.cradl.ai/api-reference/get-documents-1 /api-reference/openapi.json get /documents/{documentId} Get Document # List Functions Source: https://docs.cradl.ai/api-reference/get-functions /api-reference/openapi.json get /functions List Functions # Get Function Source: https://docs.cradl.ai/api-reference/get-functions-1 /api-reference/openapi.json get /functions/{functionId} Get Function # List Hooks Source: https://docs.cradl.ai/api-reference/get-hooks /api-reference/openapi.json get /hooks List Hooks # Get Hook Source: https://docs.cradl.ai/api-reference/get-hooks-1 /api-reference/openapi.json get /hooks/{hookId} Get Hook # List Hook Runs Source: https://docs.cradl.ai/api-reference/get-hooks-runs /api-reference/openapi.json get /hooks/{hookId}/runs List Hook Runs # Get Hook Run Source: https://docs.cradl.ai/api-reference/get-hooks-runs-1 /api-reference/openapi.json get /hooks/{hookId}/runs/{runId} Get Hook Run # List Logs Source: https://docs.cradl.ai/api-reference/get-logs /api-reference/openapi.json get /logs List Logs # Get Log Source: https://docs.cradl.ai/api-reference/get-logs-1 /api-reference/openapi.json get /logs/{logId} Get Log # List Models Source: https://docs.cradl.ai/api-reference/get-models /api-reference/openapi.json get /models List Models # Get Model Source: https://docs.cradl.ai/api-reference/get-models-1 /api-reference/openapi.json get /models/{modelId} Get Model # List Organizations Source: https://docs.cradl.ai/api-reference/get-organizations /api-reference/openapi.json get /organizations List Organizations # Get Organization Source: https://docs.cradl.ai/api-reference/get-organizations-1 /api-reference/openapi.json get /organizations/{organizationId} Get Organization # List Predictions Source: https://docs.cradl.ai/api-reference/get-predictions /api-reference/openapi.json get /predictions List Predictions # Get Prediction Source: https://docs.cradl.ai/api-reference/get-predictions-1 /api-reference/openapi.json get /predictions/{predictionId} Get Prediction # List Roles Source: https://docs.cradl.ai/api-reference/get-roles /api-reference/openapi.json get /roles List Roles # Get Role Source: https://docs.cradl.ai/api-reference/get-roles-1 /api-reference/openapi.json get /roles/{roleId} Get Role # List Users Source: https://docs.cradl.ai/api-reference/get-users /api-reference/openapi.json get /users List Users # Get User Source: https://docs.cradl.ai/api-reference/get-users-1 /api-reference/openapi.json get /users/{userId} Get User # List Validations Source: https://docs.cradl.ai/api-reference/get-validations /api-reference/openapi.json get /validations List Validations # Get Validation Source: https://docs.cradl.ai/api-reference/get-validations-1 /api-reference/openapi.json get /validations/{validationId} Get Validation # List Validation Tasks Source: https://docs.cradl.ai/api-reference/get-validations-tasks /api-reference/openapi.json get /validations/{validationId}/tasks List Validation Tasks # Get Validation Task Source: https://docs.cradl.ai/api-reference/get-validations-tasks-1 /api-reference/openapi.json get /validations/{validationId}/tasks/{taskId} Get Validation Task # Headless runs and review Source: https://docs.cradl.ai/api-reference/headless-runs Run an agent, read its output, and optionally submit review feedback entirely from code. ## Introduction This page picks up where [Introduction](/api-reference/introduction) leaves off. There, you created a run and uploaded a document. Here you wait for the agent to finish, read what it extracted, and optionally submit feedback through a validation task from code — writing corrected values back exactly the way the Cradl AI app does when a person reviews a document. Use this when you want to feed corrections back at scale, or run the full pipeline without anyone opening the app. ## When you need a validation task A validation task is **optional** for a headless run. If processing succeeds and you do not want to submit feedback, read the extracted data and stop after step 4. You do not need to create a task or send a `PUT` with validated values. If a formatter or validator fails, a validation task is created automatically. Reuse that task when submitting feedback; do not create another one for the same run. For example, a date formatter can fail when it receives an invalid date. For a fully headless workflow that does not need validation checks, remove validators from your model. Formatter failures can still create tasks. Validation tasks can remain open without issues with limits, performance, or cost. However, open runs are excluded from Insights statistics, so leaving tasks open can skew those statistics. Validation has **no expiration or time window**. You can submit corrections days later. ## Before you begin You need an access token (see [Introduction](/api-reference/introduction)) and the ID of your agent. If you plan to submit feedback in steps 5–7, you also need the agent's validation ID. The validation ID is in the agent's `resourceIds`: ```bash theme={null} curl -X GET https://api.cradl.ai/v1/agents/ \ -H "Authorization: Bearer " ``` ```json theme={null} { "agentId": "cradl:agent:xxx", "resourceIds": [ "cradl:model:xxx", "cradl:validation:xxx", "cradl:action:xxx" ], ... } ``` Take the entry starting with `cradl:validation:` — that is the `` used in steps 5 and 7. ## The flow Create a run on your agent. Keep the `id` from the response — it is on the form `//` and is the `agentRunId` that every later call needs. ```bash curl theme={null} curl -X POST https://api.cradl.ai/v1/agents//runs \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"variables": {"docId": "ABC", "customerId": "123"}}' ``` ```python Python theme={null} from cradl import Client client = Client() # reads ~/.cradl/credentials.json or CRADL_* env vars agent_run = client.create_agent_run( '', variables={'docId': 'ABC', 'customerId': '123'}, ) agent_run_id = f"{agent_run['agentId']}/{agent_run['runId']}" ``` `variables` is optional. It does not affect predictions — it is passed through and returned with the agent's output. Create a document attached to the run, then upload the file bytes to the `fileUrl` from the response. Save two more fields from that response: `documentId` and **`annotationFileUrl`**. You need the annotation file URL in steps 4 and 6. ```bash curl theme={null} curl -X POST https://api.cradl.ai/v1/documents \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "My invoice", "contentType": "application/pdf", "agentRunId": "" }' # Then upload the file to the fileUrl from the response curl -X PUT --data-binary @invoice.pdf \ \ -H "Authorization: Bearer " \ -H "Content-Type: application/pdf" ``` ```python Python theme={null} document = client.create_document( 'invoice.pdf', agent_run_id=agent_run_id, ) annotation_file_url = document['annotationFileUrl'] ``` The Python SDK's `create_document` performs both the `POST /documents` call and the file upload for you, and deletes the document again if the upload fails. Poll the run until its `status` is `completed` or `ready-for-review`. A `completed` run has finished processing; `ready-for-review` means a validation task is waiting for input. ```bash curl theme={null} curl -X GET https://api.cradl.ai/v1/agents//runs/ \ -H "Authorization: Bearer " ``` ```python Python theme={null} import time def wait_for_run(client, agent_id, run_id, timeout=300, interval=2): deadline = time.time() + timeout while time.time() < deadline: run = client.get_agent_run(agent_id, run_id) if run['status'] in ('ready-for-review', 'completed'): return run if run['status'] == 'error': raise RuntimeError(f"Run failed: {run['runId']}") time.sleep(interval) raise TimeoutError(f'Run did not finish within {timeout}s') agent_run = wait_for_run(client, agent_run['agentId'], agent_run['runId']) ``` | Status | Meaning | | - | - | | `running` | Predictions are in progress | | `ready-for-review` | A validation task is waiting for input | | `review-in-progress` | The review has been opened | | `completed` | The run has finished | | `error` | The run failed | | `archived` | The run has been archived | Polling is the simplest option, but not the only one. To be notified instead of polling, use a [Webhook export](/integrations/webhook), or configure notifications on the validation itself, which can fire when a review task is created or cancelled, or when a run fails. There are two files to read, and they serve different purposes. Both live on the file server and are fetched with a plain `GET` and the same `Authorization` header. **The run's variables file** (`variablesFileUrl` on the run) is the run payload — the same data your export actions receive. Values are either plain values or objects with a `value` key. **The document's annotation file** (`annotationFileUrl` from step 2) holds the per-field annotations that the review UI edits. This is the file you write back in step 6. ```bash curl theme={null} # The run payload curl -X GET \ -H "Authorization: Bearer " # The annotations the model produced curl -X GET \ -H "Authorization: Bearer " ``` ```python Python theme={null} import json import requests # The run payload, via the public helper variables = client.get_agent_run( agent_run['agentId'], agent_run['runId'], get_variables=True, )['variables'] # The annotations. The Python SDK has no public file server method yet, # so this uses the internal helper. annotations = json.loads(client._make_fileserver_request( requests_fn=requests.get, file_url=annotation_file_url, query_params={}, ).decode()) ``` The JavaScript SDK exposes `makeFileServerGetRequest` and `makeFileServerPutRequest` for these reads and writes. Skip steps 5–7 if you do not want to submit feedback. If a task was created automatically because a formatter or validator failed, use its `taskId` in step 7 and continue to step 6 without creating another task. Otherwise, create a task for your feedback. `input` is required — send an empty object unless you have something to put there. ```bash curl theme={null} curl -X POST https://api.cradl.ai/v1/validations//tasks \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "input": {}, "agentRunId": "" }' ``` ```python Python theme={null} validation_task = client.create_validation_task( '', input={}, agent_run_id=agent_run_id, ) task_id = validation_task['taskId'] ``` The response contains a `taskId` on the form `cradl:task:xxx` and `status: "ready"`. Correct the annotations you read in step 4 and `PUT` the whole object back to the document's `annotationFileUrl` as JSON. See [Annotation file format](#annotation-file-format) below for the structure. ```bash curl theme={null} curl -X PUT --data-binary @annotations.json \ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" ``` ```python Python theme={null} annotations['invoiceDate'] = [{'value': '2021-05-01'}] client._make_fileserver_request( requests_fn=requests.put, file_url=annotation_file_url, content=json.dumps(annotations).encode(), ) ``` The annotation `PUT` in step 6 does not close the task. If you leave an existing task as is, it remains open. To complete it, patch the task to `succeeded`. Send the same annotations object as `output` — the file you wrote in step 6 is the working copy, while `output` is what the completed task carries. ```bash curl theme={null} curl -X PATCH https://api.cradl.ai/v1/validations//tasks/ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "output": { "invoiceDate": [{ "value": "2021-05-01" }] }, "status": "succeeded" }' ``` ```python Python theme={null} client.update_validation_task( '', task_id, output=annotations, status='succeeded', ) ``` The task accepts `output`, `metadata`, `status`, `name` and `description` on a patch. To reject a document rather than approve it, cancel the task and say why: ```json theme={null} { "output": { "statusReason": "rejected" }, "status": "cancelled" } ``` ## Annotation file format The annotation file is a JSON object keyed by the field IDs defined in your agent's model. ```json theme={null} { "invoiceDate": [ { "value": "2021-05-01", "rawValue": "01.05.21", "confidence": 0.89, "page": 0, "id": "annotation-1" } ], "tags": [ { "value": "urgent", "rawValue": "urgent", "confidence": null, "page": null, "id": null }, { "value": "eu", "rawValue": "eu", "confidence": null, "page": null, "id": null } ], "lineItems": [ { "description": [{ "value": "Consulting", "rawValue": "Consulting", "confidence": null, "page": null, "id": null }], "amount": [{ "value": "20.50", "rawValue": "20,50", "confidence": null, "page": null, "id": null }] } ] } ``` * Every field maps to an **array**. A single-value field is an array with one element, a multi-value field has one element per value, and a table field is an array of row objects keyed by column ID. * Each annotation has up to five keys: `value`, `rawValue`, `confidence`, `page` and `id`. `null` is allowed for all of them, so a hand-written correction can be as short as `{"value": "2021-05-01"}`. * Annotation files produced by the model also carry read-only fields such as `location` (four normalized numbers describing the bounding box) and `source`. These are dropped when the file is overwritten, so do not rely on a write preserving them. ## Complete example This example reads the extracted data by default. Set `SUBMIT_FEEDBACK` to `True` to submit feedback and apply your corrections where indicated. If a validation task already exists, set `EXISTING_TASK_ID` to its ID to reuse it. The code example below has been generated by an AI. Use it as a starting point and **verify correctness** before using in production. ```python theme={null} import json import time import requests from cradl import Client AGENT_ID = 'cradl:agent:xxx' VALIDATION_ID = 'cradl:validation:xxx' DOCUMENT_PATH = 'invoice.pdf' SUBMIT_FEEDBACK = False EXISTING_TASK_ID = None # Set to the existing task's ID if one was created automatically. client = Client() agent_run = client.create_agent_run(AGENT_ID) agent_run_id = f"{agent_run['agentId']}/{agent_run['runId']}" document = client.create_document(DOCUMENT_PATH, agent_run_id=agent_run_id) annotation_file_url = document['annotationFileUrl'] while True: agent_run = client.get_agent_run(agent_run['agentId'], agent_run['runId']) if agent_run['status'] in ('ready-for-review', 'completed'): break if agent_run['status'] == 'error': raise RuntimeError(f"Run failed: {agent_run['runId']}") time.sleep(2) variables = client.get_agent_run( agent_run['agentId'], agent_run['runId'], get_variables=True, )['variables'] print(json.dumps(variables, indent=2)) annotations = json.loads(client._make_fileserver_request( requests_fn=requests.get, file_url=annotation_file_url, query_params={}, ).decode()) print(json.dumps(annotations, indent=2)) if SUBMIT_FEEDBACK: task_id = EXISTING_TASK_ID if task_id is None: if agent_run['status'] == 'ready-for-review': raise ValueError('Set EXISTING_TASK_ID to the task already waiting for review.') validation_task = client.create_validation_task( VALIDATION_ID, input={}, agent_run_id=agent_run_id, ) task_id = validation_task['taskId'] # Apply your corrections to `annotations` here, then write them back. client._make_fileserver_request( requests_fn=requests.put, file_url=annotation_file_url, content=json.dumps(annotations).encode(), ) client.update_validation_task( VALIDATION_ID, task_id, output=annotations, status='succeeded', ) ``` # Introduction Source: https://docs.cradl.ai/api-reference/introduction Fundamental concepts of Cradl’s API. > **Note**\ > You can access our API directly from the Cradl AI App without any additional credentials—just sign up to get started. ## Base URL Cradl’s API follows REST principles and is served over HTTPS. For security, unencrypted HTTP is not supported. All API endpoints use the following base URL: ``` https://api.cradl.ai/v1 ``` ## Authentication To authenticate with Cradl’s API, use the `Authorization` header with a **Bearer token**. You can obtain an access token by making the following request: ```bash theme={null} curl -X POST https://auth.cradl.ai/oauth2/token \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data 'grant_type=client_credentials&audience=https://api.cradl.ai/v1&client_id=&client_secret=' ``` The response will include an access token: ```json theme={null} { "access_token": "", ... } ``` Use this token in **all subsequent API requests**: ```http theme={null} Authorization: Bearer ``` *** ## Creating a Run You can find your agent ID in the Cradl AI UI. It will always be on the form \`cradl:agent:xxx'. Alternatively, you can list your agents using the API: ```bash theme={null} curl -X GET https://api.cradl.ai/v1/agents \ -H "Authorization: Bearer " ``` Example response: ```json theme={null} { "agents": [ { "agentId": "", ... } ], "nextToken": null } ``` Once you have the agent ID on the form \`cradl:agent:xxx', create a run: ```bash theme={null} curl -X POST https://api.cradl.ai/v1/agents//runs \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{}' ``` **Add variables:** Variables are additional information you can send with a run. They do not affect predictions and are returned with the rest of the agent’s output. Let's say you want to add some internal document Id and a customer Id for the document, just replace `-d '{}'` in the request above with: ```json theme={null} -d '{"variables": {"docId": "ABC", "customerId": "123" }}' ``` Example response: ```json theme={null} { "id": "//", ... } ``` Create a document and associate it with your run: ```bash theme={null} curl -X POST https://api.cradl.ai/v1/documents \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "My invoice", "agentRunId": "//" }' ``` This response includes a `fileUrl` on the form [https://files.api.cradl.ai/las:file:xxx](https://files.api.cradl.ai/las:file:xxx) that you’ll use to upload your document file: ```bash theme={null} curl -X PUT --data-binary @test.jpeg \ \ -H "Authorization: Bearer " \ -H "Content-Type: image/jpeg" ``` ## Next steps To wait for the run to finish, read what the agent extracted, and optionally submit review feedback from code, continue with [Headless runs and review](/api-reference/headless-runs). # Update Action Source: https://docs.cradl.ai/api-reference/patch-actions /api-reference/openapi.json patch /actions/{actionId} Update Action # Update Action Run Source: https://docs.cradl.ai/api-reference/patch-actions-runs /api-reference/openapi.json patch /actions/{actionId}/runs/{runId} Update Action Run # Update Agent Source: https://docs.cradl.ai/api-reference/patch-agents /api-reference/openapi.json patch /agents/{agentId} Update Agent # Update Agent Run Source: https://docs.cradl.ai/api-reference/patch-agents-runs /api-reference/openapi.json patch /agents/{agentId}/runs/{runId} Update Agent Run # Update Appclient Source: https://docs.cradl.ai/api-reference/patch-appclients /api-reference/openapi.json patch /appClients/{appClientId} Update Appclient # Update Document Source: https://docs.cradl.ai/api-reference/patch-documents /api-reference/openapi.json patch /documents/{documentId} Update Document # Update Function Source: https://docs.cradl.ai/api-reference/patch-functions /api-reference/openapi.json patch /functions/{functionId} Update Function # Update Hook Source: https://docs.cradl.ai/api-reference/patch-hooks /api-reference/openapi.json patch /hooks/{hookId} Update Hook # Update Hook Run Source: https://docs.cradl.ai/api-reference/patch-hooks-runs /api-reference/openapi.json patch /hooks/{hookId}/runs/{runId} Update Hook Run # Update Model Source: https://docs.cradl.ai/api-reference/patch-models /api-reference/openapi.json patch /models/{modelId} Update Model # Update Organization Source: https://docs.cradl.ai/api-reference/patch-organizations /api-reference/openapi.json patch /organizations/{organizationId} Update Organization # Update Role Source: https://docs.cradl.ai/api-reference/patch-roles /api-reference/openapi.json patch /roles/{roleId} Update Role # Update User Source: https://docs.cradl.ai/api-reference/patch-users /api-reference/openapi.json patch /users/{userId} Update User # Update Validation Source: https://docs.cradl.ai/api-reference/patch-validations /api-reference/openapi.json patch /validations/{validationId} Update Validation # Update Validation Task Source: https://docs.cradl.ai/api-reference/patch-validations-tasks /api-reference/openapi.json patch /validations/{validationId}/tasks/{taskId} Update Validation Task # Create Action Source: https://docs.cradl.ai/api-reference/post-actions /api-reference/openapi.json post /actions Create Action # Create Action Run Source: https://docs.cradl.ai/api-reference/post-actions-runs /api-reference/openapi.json post /actions/{actionId}/runs Create Action Run # Create Agent Source: https://docs.cradl.ai/api-reference/post-agents /api-reference/openapi.json post /agents Create Agent # Create Agent Run Source: https://docs.cradl.ai/api-reference/post-agents-runs /api-reference/openapi.json post /agents/{agentId}/runs Create Agent Run # Create Appclient Source: https://docs.cradl.ai/api-reference/post-appclients /api-reference/openapi.json post /appClients Create Appclient # Create Document Source: https://docs.cradl.ai/api-reference/post-documents /api-reference/openapi.json post /documents Create Document # Create Function Source: https://docs.cradl.ai/api-reference/post-functions /api-reference/openapi.json post /functions Create Function # Create Hook Source: https://docs.cradl.ai/api-reference/post-hooks /api-reference/openapi.json post /hooks Create Hook # Create Hook Run Source: https://docs.cradl.ai/api-reference/post-hooks-runs /api-reference/openapi.json post /hooks/{hookId}/runs Create Hook Run # Create Model Source: https://docs.cradl.ai/api-reference/post-models /api-reference/openapi.json post /models Create Model # Create Organization Source: https://docs.cradl.ai/api-reference/post-organizations /api-reference/openapi.json post /organizations Create Organization # Create Prediction Source: https://docs.cradl.ai/api-reference/post-predictions /api-reference/openapi.json post /predictions Create Prediction # Create Role Source: https://docs.cradl.ai/api-reference/post-roles /api-reference/openapi.json post /roles Create Role # Create User Source: https://docs.cradl.ai/api-reference/post-users /api-reference/openapi.json post /users Create User # Create Validation Source: https://docs.cradl.ai/api-reference/post-validations /api-reference/openapi.json post /validations Create Validation # Create Validation Task Source: https://docs.cradl.ai/api-reference/post-validations-tasks /api-reference/openapi.json post /validations/{validationId}/tasks Create Validation Task # Roles and Permissions Source: https://docs.cradl.ai/api-reference/roles Cradl AI uses a role-based access model to manage what users can see and do in the platform. Each user is assigned a role that determines their level of access to features and workflows. ## Available Roles ### **Admin** Suited for team leads, project managers, or organization owners who need oversight of the entire platform. * Full access to all features in Cradl AI. * Can manage organization settings, user accounts, and billing. * Can assign and update user roles. ### **Member** Ideal for technical team members responsible for building and maintaining AI workflows. * Can create, edit, and manage AI agents. * Can configure and update workflows. * Can view and review flagged documents, but their primary responsibility is building and maintaining the AI logic. ### **Validator** Perfect for subject matter experts, analysts, or operations staff who need to validate extracted data without technical access. * Can approve, reject, or correct extracted data, helping to improve the model’s accuracy over time. * Cannot modify AI agents, workflows, or organization settings. # Classification Source: https://docs.cradl.ai/core-concepts/ai-model/classification-formatter Restrict a field to a set of categories. The **Classification** formatter restricts a field to a list of categories you define. It is how you keep a field like document type or status to a fixed vocabulary that downstream systems can rely on. Common uses are document type (`Invoice`, `Receipt`, `Credit note`) and yes/no states (`Signed`, `Not signed`). ## Options | Option | What it does | | - | - | | **Categories** | The allowed values. Each one can be given a description, which tells the model what belongs in that category. | | **Default value** | A category to fall back on when the extracted value is not one of the others. Optional. | Descriptions are worth filling in when the category name alone is ambiguous. For a `Credit note` category, a description such as *"a negative invoice that refunds an earlier one"* gives the model something to go on. ## How values are handled | Extracted value | Without a default value | With default value `Unknown` | | - | - | - | | `Invoice` — an exact category | `Invoice` | `Invoice` | | `Faktura` — not a category | Error, field cleared | `Unknown` | Matching is exact, so the value must be one of the categories as written. If you also need to fold variations and spellings into a category, use [Match & Map](/core-concepts/ai-model/match-and-map), which does fuzzy matching. ## When it fails Without a default value, a value outside the category list clears the field and reports an error listing the allowed categories, which sends the document to human review. With a default value set, nothing fails — unrecognized values become the default, so make sure that is what you want before setting one. # Confidence threshold Source: https://docs.cradl.ai/core-concepts/ai-model/confidence-validator Send a field to review when the model is not sure enough. The **Confidence threshold** validator sets the minimum confidence the model must have in a field for it to be auto-approved. Anything below the threshold is flagged, so a human looks at the values the model was unsure about and the rest pass through untouched. This is the main dial between automation rate and accuracy. A high threshold catches more mistakes but sends more documents to review; a low one automates more and lets more mistakes through. ## Options | Option | What it does | | - | - | | **Threshold** | The minimum confidence, as a percentage. Pick one of the presets or set a custom value with the slider. | Rather than guessing, run a batch of documents through and look at how often the model is right at each confidence level. Start strict, then lower the threshold on fields that prove reliable. ## How it behaves | Situation | Result | | - | - | | Confidence at or above the threshold | Passes | | Confidence below the threshold | Flagged for review | | The field is empty | Passes — use [Cannot be empty](/core-concepts/ai-model/validators) to require a value | | The reviewer already edited the field | Skipped, since the value is now human-entered | This validator always reports a **warning**, so the reviewer can accept a low-confidence value as it stands. The warning/error setting shown in the panel has no effect here — if a field must be corrected before the review can be completed, enforce it with a validator that supports errors, such as [RegExp](/core-concepts/ai-model/regex-validator) or Cannot be empty. Setting every confidence threshold to `0` makes every run stop for human review, which is occasionally useful when you want a person to see everything. # Date Source: https://docs.cradl.ai/core-concepts/ai-model/date-formatter Convert dates written in any style or language into ISO 8601. The **Date** formatter converts a date into ISO 8601 (`YYYY-MM-DD`), whichever way it is written in the document. Dates written in other languages are recognized too, so `15. mars 2024` and `5 janvier 2024` both work. ``` Jan 5, 2024 -> 2024-01-05 15. mars 2024 -> 2024-03-15 15.03.2024 -> 2024-03-15 ``` ## Options | Option | What it does | | - | - | | **Select output date format** | `YYYY-MM-DD` — currently the only output format. | | **Choose date format for ambiguous dates** | How to read dates where the day and month could be swapped, such as `03/04/2024`. Pick **Auto detect**, **Month first (US)** or **Day first (EU)**. | ### Ambiguous dates A date like `03/04/2024` is the 3rd of April in Europe and the 4th of March in the US. The setting decides which one it is: | Setting | `03/04/2024` becomes | | - | - | | Auto detect | `2024-03-04` | | Month first (US) | `2024-03-04` | | Day first (EU) | `2024-04-03` | **Auto detect** currently reads ambiguous numeric dates month-first, the same as **Month first (US)**. If your documents are European, choose **Day first (EU)** explicitly rather than relying on auto-detection. Dates that are not ambiguous are unaffected by this setting. `15.03.2024` and `2024-03-15` both become `2024-03-15` whichever option you pick, because no valid month is `15`. ## Supported languages Dates are first read as English, then tried against Spanish, German, French, Dutch, Polish, Italian, Portuguese, Danish, Finnish, Norwegian, Swedish, Russian, Japanese and Chinese (simplified and traditional). ## When it fails If no date can be read from the value, the field is cleared and the formatter reports `Unable to parse as a date`, sending the document to human review. An empty value is left empty without an error. # Format validators Source: https://docs.cradl.ai/core-concepts/ai-model/format-validators Check that a value is a well-formed identifier. Format validators check that a value is a well-formed identifier of a given kind — an IBAN, an email address, a license plate. They need no configuration: add one to a field and it validates the format. | Validator | Checks that the value is | Can be limited to | | - | - | - | | **IBAN** | A valid International Bank Account Number | Countries | | **BIC/SWIFT** | A valid Bank Identifier Code | — | | **VAT ID** | A valid VAT identification number (68 countries) | Countries | | **Tax ID** | A valid tax identification number (36 locales) | Locales | | **Email** | A valid email address | — | | **URL** | A valid web address | — | | **Phone Number** | A valid phone number | Locales | | **Passport Number** | A valid passport number (60 countries) | Countries | | **License Plate** | A valid vehicle license plate (12 locales) | Locales | | **Freight Container ID** | A valid ISO 6346 freight container ID | — | ## Limiting to countries or locales Several of these accept values from many countries, in formats that differ from one country to the next. Left alone, they accept a value that is valid in **any** supported country — so a Norwegian organization number may pass a VAT ID check because it is a valid format somewhere else. Use the **Countries** or **Locales** setting on those validators to list the ones you actually receive documents from. The check then only accepts those formats, which catches far more genuine mistakes. These validators check the *format*, not whether the identifier exists. A made-up IBAN with a correct check digit passes. For checks against a real register, validate the value in your own system after export, or fetch the valid values with [Match & Map](/core-concepts/ai-model/match-and-map). ## How they behave | Situation | Result | | - | - | | The value is well formed | Passes | | The value is not well formed | Flagged, with the validator's message or your own | | The field is empty | Passes — use [Cannot be empty](/core-concepts/ai-model/validators) to require a value | Spaces are ignored before checking, so `NO93 8601 1117 947` is treated the same as `NO9386011117947`. License plates ignore hyphens as well. Like every validator, each of these can be set to show a warning or an error, and can carry your own message — see [Validators](/core-concepts/ai-model/validators#settings). # Formatters Source: https://docs.cradl.ai/core-concepts/ai-model/formatters Transform and normalize extracted values. Formatters help you clean up and standardize extracted values — so your data is consistent, structured, and ready for use in downstream systems. They run after extraction, allowing your model to focus on finding the right values while the formatter ensures they’re in the right shape. *** ### Available Formatters * [**Numeric**](/core-concepts/ai-model/numeric-formatter)\ Turns numbers written any way into a plain number, dropping currency symbols, spaces and thousand separators. * [**Date**](/core-concepts/ai-model/date-formatter)\ Converts dates into ISO 8601 (`YYYY-MM-DD`), including dates written in other languages. * [**RegExp**](/core-concepts/ai-model/regex-formatter)\ Rewrites a value with a regular expression. Use it to strip prefixes, remove separators, or keep only part of a value. * [**Classification**](/core-concepts/ai-model/classification-formatter)\ Restricts a field to a set of categories, such as document type or a signed/not signed state. * [**Match & Map**](/core-concepts/ai-model/match-and-map)\ Matches the value against a list of known values and optionally replaces it with a mapped value. The list can be edited in a table, imported from CSV, or fetched from your own URL. Some formatters are added for you rather than picked from the list. When your agent is built, fields that hold a currency, a country or a gender get a **Currency**, **ISO Alpha-2 country code** or **Gender** formatter, which enforces the valid values for that field and falls back to `Unknown`. ### How formatters run A field can have several formatters. They run in the order they are listed, each one receiving the value the previous one produced — so a RegExp formatter that strips a currency symbol can be followed by a Numeric formatter that parses what is left. If a formatter cannot produce a value — an unparseable number, a date it does not recognize, a value missing from a classification — it clears the field and reports an error, and the remaining formatters are skipped. The document then goes to human review instead of continuing with a wrong value. A field with no formatters keeps the value exactly as it was extracted. # Logical expression Source: https://docs.cradl.ai/core-concepts/ai-model/logical-expression-validator Check that numbers across fields add up. The **Logical expression** validator checks a numeric relationship between fields. It is how you catch documents where each field looks fine on its own but the numbers do not agree — a total that does not match its line items, a discount larger than the price. ``` net_amount + vat_amount = total_amount total_amount > 0 ``` ## Options You build two expressions and choose how to compare them. | Option | What it does | | - | - | | **Left and right expression** | Each is built from field values, plain numbers, and the `+`, `-`, `*` and `/` operators. | | **Operator** | How the two sides are compared: `=`, `<`, `>`, `≤` or `≥`. | ## How it behaves | Situation | Result | | - | - | | The comparison holds | Passes | | The comparison does not hold | Flagged — `Logical expression not satisfied`, or your own message | | A field in the expression has no value | Flagged — the check cannot be evaluated | | A field in the expression is not a number | Flagged — `Got non-numeric value in logical expression` | Because a missing value makes the check impossible to evaluate, pair this with [Cannot be empty](/core-concepts/ai-model/validators) on the fields involved if they are required. The comparison is numeric. Fields used in the expression should have a [Numeric formatter](/core-concepts/ai-model/numeric-formatter) so that values like `1 234,56` are turned into numbers before the check runs. Dates and text cannot be compared this way. Totals rarely match to the last decimal once rounding is involved. If a strict `=` flags documents that are actually correct, compare against a small tolerance instead — for example check that the difference is `≤ 0.01`. # Match & Map Source: https://docs.cradl.ai/core-concepts/ai-model/match-and-map Match extracted values against a list of known values, and optionally replace them. The **Match & Map** formatter compares an extracted value against a list of values you provide, and replaces it with the value you have mapped it to. Use it to normalize messy values — supplier names, product codes, departments — or to restrict a field to a known set of values. It is useful in two ways: * **Match and map** — the document says `coca cola`, your system needs `The Coca-Cola Company`. * **Match only** — you only want to accept values that exist in your list, and leave them as they are. Both are configured with the same mapping, described below. ## Mapping format A mapping is a **JSON array** of objects. Each object has a `from` and an optional `to`: ```json theme={null} [ { "from": "coca cola", "to": "The Coca-Cola Company" }, { "from": "Coca-Cola Co", "to": "The Coca-Cola Company" }, { "from": "pepsico inc", "to": "PepsiCo, Inc." } ] ``` * **`from`** is the value to look for in the document. Rows without a `from` are ignored. * **`to`** is the value the field is set to when the row matches. It is optional — leave it out and the field keeps the `from` value, which is how you build a match-only list: ```json theme={null} [ { "from": "Invoice" }, { "from": "Credit note" }, { "from": "Receipt" } ] ``` Several rows may point at the same `to`, as in the first example — that is how you fold many spellings into one canonical value. You can also mix mapped and unmapped rows in the same list. ## Providing the mapping Edit the **From** and **To** columns directly in the formatter. This is the simplest option when the list is short and changes rarely. Use **Import from CSV** and pick which column is `from` and which is `to`. The file is read once at import and turned into a mapping — later changes to the CSV file have no effect. Use **Connect with URL** to fetch the mapping from your own endpoint every time a document is processed. See [Connecting a URL](#connecting-a-url). ## Connecting a URL When you connect a URL, Cradl AI calls your endpoint each time a document is processed and uses the response as the mapping. This keeps the list in sync with your own system — a supplier register or product catalogue — without re-importing anything. In **Connect with URL** you set the endpoint and choose `GET` or `POST`. While a URL is connected the mapping table is read-only, since the endpoint is the source of truth. ### Expected response Your endpoint must answer with a success status and a JSON body that is **the mapping array itself** — the same format as above, with nothing wrapped around it. ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json [ { "from": "coca cola", "to": "The Coca-Cola Company" }, { "from": "Coca-Cola Co", "to": "The Coca-Cola Company" }, { "from": "pepsico inc", "to": "PepsiCo, Inc." } ] ``` A wrapped response does **not** work. The body must be the array, not an object containing it: ```json theme={null} { "data": [{ "from": "coca cola", "to": "The Coca-Cola Company" }] } ``` Neither does an object keyed by value: ```json theme={null} { "coca cola": "The Coca-Cola Company" } ``` You can check your endpoint with: ```bash theme={null} curl -s | head -c 200 ``` The output should start with `[` and the first object should have a `from` key. The endpoint is called on every document, so it needs to be publicly reachable and reasonably fast. The dialog takes a URL and an HTTP method only, so the endpoint cannot require custom authentication headers. If the endpoint returns an error status, or something that is not a mapping array, the field is left empty and the formatter reports an error — which sends the document to human review rather than silently writing a wrong value. ## Fuzzy matching By default a value must match a `from` exactly. Turn on **fuzzy matching** to also accept small differences, measured as [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) — the number of single-character edits needed to turn one string into the other. * **Threshold** — how many edits are allowed. `0` means exact match. Enabling fuzzy matching in the app starts at `1`, and you can raise it. * **Ignore case and locale** — when on, differences in capitalization and accents cost nothing: `Coca Cola` and `COCA COLA` are both distance `0` from `coca cola`, and `Nestlé` is distance `0` from `nestle`. With a threshold of `1` and case differences ignored, a `from` of `foo` matches `foo`, `Foo`, `fo` and `foo6`, but not `foobar`. Raise the threshold carefully. A high threshold on short values makes unrelated entries match — with a threshold of `3`, `cat` matches almost any three-letter word. ## What the formatter does The formatter reads the extracted value, or the raw value if the field has not been formatted yet, and then: | Situation | Field value | Result | | - | - | - | | The extracted value is empty | Empty | No message | | The mapping is empty or could not be loaded | Empty | Error: `Mapping is empty` | | No row is within the threshold | Empty | Error: `No mapping match found` | | Exactly one row matches | The row's `to`, or its `from` if `to` is not set | No message | | Several rows match | The closest row's value. On a tie, the row listed first wins | Warning listing how many rows matched | An error leaves the field empty and prevents the document from being automated, so it goes to human review. A warning does not block automation — it just records that the match was ambiguous, which usually means the mapping has near-duplicate `from` values worth cleaning up. # Numeric Source: https://docs.cradl.ai/core-concepts/ai-model/numeric-formatter Turn numbers written any way into a plain, consistent number. The **Numeric** formatter turns a number as written in the document into a plain number: no currency symbols, no spaces, no thousand separators, and `.` as the decimal separator. ``` 1 234,56 -> 1234.56 USD 1,234 -> 1234 ``` ## Options | Option | What it does | | - | - | | **Numeric type** | **Float** keeps decimals, **Integer** rounds to a whole number. | | **Limit number of decimals** | Rounds to the given number of decimals and pads with zeros, so `2` turns `1234.5` into `1234.50`. Without it, trailing zeros are dropped — `1000.00` becomes `1000`. | | **Decimal separator on ambiguity** | Which of `.` or `,` to read as the decimal separator when the value could be read either way. See [Ambiguous separators](#ambiguous-separators). | ## How values are read Anything that is not a digit, separator or minus sign is removed first, so currency codes, symbols and stray text do not matter. A minus sign is kept only at the start. When a value contains **both** `.` and `,`, the last one is the decimal separator: | Value | Becomes | | - | - | | `1,234.56` | `1234.56` | | `12.345,67` | `12345.67` | | `1.234.567,89` | `1234567.89` | | `USD 1 000.00` | `1000` | | `-1 000` | `-1000` | ### Ambiguous separators When only one separator is present it may be either a decimal or a thousand separator. The formatter guesses from the digit grouping: a separator followed by exactly three digits is read as a thousand separator. | Value | Default | With separator set to **Period** | | - | - | - | | `1,5` | `1.5` | `1.5` | | `1.234` | `1234` | `1.234` | | `1234.567` | `1234567` | `1234.567` | The default is the right guess for `1.234` meaning one thousand two hundred and thirty-four, and the wrong one for a value with three decimals such as `1234.567`. If your documents have three-decimal numbers — unit prices, weights, exchange rates — set **Decimal separator on ambiguity** to the separator your documents actually use. ## When it fails If the value cannot be read as a number at all, the field is cleared and the formatter reports `Unable to parse as a number`. The document then goes to human review. An empty value is left empty without an error. # Overview Source: https://docs.cradl.ai/core-concepts/ai-model/overview Build AI models to automatically parse the information you need from any document type. Cradl AI uses Large Language Models (LLMs) that are trained to understand text much like a human would. This means your agents can handle a wide variety of documents, even if layouts, wording, or formats change from one file to another. *** ## Editing Your AI Model Go to *Workflow* [→](#) *Extract with AI* node. Edit Ai Model Web Here you can: * [**Add a prompt→**](/core-concepts/ai-model/prompting)\ Write clear instructions that tell your agent what to extract. * [**Add validators →**](/core-concepts/ai-model/validators)\ Add field-level checks to make sure extracted data is accurate. * [**Add formatters→**](/core-concepts/ai-model/formatters)\ Format outputs so your data is always structured the way you need it. * **Improve your model**\ Refine prompts, validation rules, and feedback loops to continuously improve your results. # Prompting Source: https://docs.cradl.ai/core-concepts/ai-model/prompting Build efficient prompts for extracting data from documents. Prompt design is an iterative process that benefits from experimentation. Start with simple instructions, then refine by adding context and constraints. Throughout this guide, you’ll learn how clarity, specificity, and conciseness often lead to better results. ## Be Clear and Concise AI models respond best to clear, direct language. Avoid conversational or ambiguous phrasing. **Do:** The total amount, including taxes. **Don’t:** Can you please extract the amount from my invoice? ## Include Examples Examples help the model understand the type of data to extract or generate. **Do:** The bank account number, often written as XXX.YYYY.ZZZZ. ## Provide Alternative Wordings If fields may appear with different labels, include common synonyms, abbreviations, or variations. **Do:** Vehicle registration number, sometimes listed as V/N or VR#. ## Avoid Output Formatting Instructions Avoid telling the model how to format outputs (e.g., “return the date in ISO format”). Instead, rely on built-in formatters from the formatter library to handle output presentation. # RegExp Source: https://docs.cradl.ai/core-concepts/ai-model/regex-formatter Rewrite a value with a regular expression. The **RegExp** formatter rewrites a value using a regular expression: it finds what the pattern matches and replaces it with your replacement. Use it to strip prefixes, remove separators, or pull one part out of a longer value. ``` INV-1023 -> 1023 (match "INV-", no replacement) NO 123 456 789 -> NO123456789 (match "\s", no replacement) ``` ## Options | Option | What it does | | - | - | | **Match pattern** | Required. A JavaScript regular expression. Everything it matches is replaced. | | **Replace pattern** | What to put in place of each match. Leave it empty to delete the match. Capture groups are available as `$1`, `$2`, and so on. | | **Match flags** | JavaScript regular expression flags. Defaults to `gs` — `g` replaces every match rather than just the first, `s` lets `.` match newlines. | ## Examples | Goal | Match pattern | Replace pattern | `Total: 1234 NOK` becomes | | - | - | - | - | | Strip a prefix | `INV-` | *(empty)* | — | | Remove all spaces | `\s` | *(empty)* | `Total:1234NOK` | | Keep only the number | `^\D*(\d+).*$` | `$1` | `1234` | | Swap separators | `-` | `/` | — | This formatter **replaces** rather than extracts. To keep only part of a value, write a pattern that matches the whole value, put the part you want in a capture group, and use `$1` as the replacement — as in the third row above. ## When it fails If the pattern or the flags are not valid, the field is cleared and the formatter reports `RegEx Formatter: Invalid pattern or flags specified`, which sends the document to human review. A pattern that simply does not match anything is not an error — the value is passed through unchanged. An empty value is left empty. This is the formatter, which rewrites values. To check a value against a pattern without changing it, use the [RegExp validator](/core-concepts/ai-model/regex-validator) instead. # RegExp Source: https://docs.cradl.ai/core-concepts/ai-model/regex-validator Check that a value matches a pattern. The **RegExp** validator checks a value against a regular expression without changing it. Use it for values that have to follow a known shape — reference numbers, postal codes, internal identifiers. ## Options | Option | What it does | | - | - | | **Match pattern** | Required. A JavaScript regular expression the value must match. | | **Match flags** | JavaScript regular expression flags. Defaults to `gs`. | The pattern has to *match*, not match the whole value, so anchor it with `^` and `$` when the whole value must conform. `^\d{4}$` accepts only a four-digit value, while `\d{4}` accepts anything containing four digits in a row. ## Examples | Goal | Match pattern | | - | - | | Exactly four digits | `^\d{4}$` | | An invoice number like `INV-1023` | `^INV-\d+$` | | A Norwegian postal code | `^\d{4}$` | | A value containing no letters | `^[^A-Za-z]*$` | ## How it behaves | Situation | Result | | - | - | | The value matches | Passes | | The value does not match | Flagged — `Text does not match expected pattern`, or your own message | | The pattern or flags are invalid | Flagged — `RegEx Validator: Invalid pattern or flags specified` | | The field is empty | Passes — use [Cannot be empty](/core-concepts/ai-model/validators) to require a value | This is the validator, which checks values and leaves them alone. To rewrite a value with a regular expression, use the [RegExp formatter](/core-concepts/ai-model/regex-formatter) instead. # Validators Source: https://docs.cradl.ai/core-concepts/ai-model/validators Automatically validate fields. Validators let you define rules that check whether extracted fields meet your requirements — so you only review documents that actually need attention. This helps you reduce manual work and improve data quality at scale. *** ### Available Validators * [**Confidence threshold**](/core-concepts/ai-model/confidence-validator)\ Flags a field when the model’s confidence is below a percentage you set. The main dial between automation rate and accuracy. * **Cannot be empty**\ Flags the document when a required field is missing or empty. Use this to make sure essential information is always extracted. * [**Logical expression**](/core-concepts/ai-model/logical-expression-validator)\ Checks a numeric relationship between fields, such as `net_amount + vat_amount = total_amount`. Catches documents where the numbers don’t add up. * [**RegExp**](/core-concepts/ai-model/regex-validator)\ Checks that a value matches a regular expression. Good for reference numbers, postal codes and other fixed shapes. * [**Format validators**](/core-concepts/ai-model/format-validators)\ Ready-made checks for well-known identifiers: IBAN, BIC/SWIFT, VAT ID, Tax ID, email, URL, phone number, passport number, license plate and freight container ID. ### Settings Every validator has the same two settings, so you control how strict it is and what the reviewer sees. **If validation fails** decides what happens when the check does not pass: * **Show warning** — the reviewer sees the message but can accept the value anyway. Use for rules that are usually right but have legitimate exceptions. * **Show error** — the value must be corrected before the review can be completed. Use for rules that must always hold. **Customize the message** replaces the default text with your own, which is what the reviewer reads in the validation interface. A specific message such as *"The line items don't add up to the total — check for a missing row"* is easier to act on than a generic one. The [confidence threshold](/core-concepts/ai-model/confidence-validator) validator always reports a warning, so a low-confidence value can be accepted as-is by the reviewer regardless of this setting. ### Validators and formatters Validators check values; [formatters](/core-concepts/ai-model/formatters) change them. Formatters run first, so a validator always sees the formatted value — which is why a numeric check works on `1 234,56` only if a Numeric formatter has turned it into `1234.56` first. # Human-in-the-loop Source: https://docs.cradl.ai/core-concepts/human-in-the-loop Sometimes the AI needs a litte bit of human touch. Cradl AI’s built-in Human-in-the-Loop (HITL) functionality makes it easy for humans to review predictions the AI is uncertain of. integrating humans into the AI process flow. The goal is to use human input to maximise the potential of AI while mitigating its risks. By making use of the integrating human review and corrections, you can ensure that the data extracted by your AI agents is accurate and reliable before it’s used in critical business processes. Additionally, your AI agent will learn from each human correction to improve the model AI Review By Human Web ### Key Features * **Review interface**\ Purpose-built UI for document processing cues and smart tools reduce handling time per document and make it easy for reveiwers to work efficiently. * **Confidence threshold filters**\ Automatically filter low-confidence extractions to limit how many documents require human review. You can adjust the confidence level for each * **User management**\ You can invite dedicated members to your AI agent as ***Reviewers***. These members will only have access to the Runs tab and the Review section in the platfrom. *** ### Benefits * **Risk Mitigation**\ Minimize financial and compliance risks by ensuring critical data , like invoice amounts, billing addresses, or loan details, is correct before it moves downstream. * **Simplified Exception Handling**\ Quickly roll out a reliable human review and exception-handling workflow without complex setup. * **Workforce Efficiency**\ Track, manage, and boost the productivity of your human review team with built-in insights. * **Cost Control**\ Control the costs of human review by using configurable confidence filters and task management. * **Data Completeness**\ Ensure the extracted data is accurate and complete before it’s used in your business systems. # Runs Source: https://docs.cradl.ai/core-concepts/runs The Runs page is the front screen of Cradl AI - your main hub for tracking and managing all the documents processed through your AI agent. Runs Web ### What you can do in Runs The Runs section makes it easy to: * **See all processed documents**\ Get a complete overview of every document that has run through your workflows — including when it was uploaded, which model was used, and the current status. * **Check status at a glance**\ Instantly see whether a document was processed successfully, if it needs manual review, or if something failed. * **Upload new documents**\ Drag and drop files directly or use the upload button to add new documents for processing. * **Open flagged documents**\ If a document didn’t meet your confidence threshold or failed validation, it will appear here as **Waiting for review**. * **Start the review process**\ Click any flagged document to open it in the dedicated review UI. Here, you can correct or confirm extracted fields to improve accuracy and retrain your model. # Workflow builder Source: https://docs.cradl.ai/core-concepts/workflow The Workflow section in Cradl AI allows you to build complex automations that interact with your data sources. Workflow Web In the workflow section, you can: * Trigger automatic document intake from different sources * Edit your data extraction AI model * Add custom validation and business logic * Route low-confidence predictions for manual review * Export transformed data to your systems automatically *** ## How workflows work A Workflow in Cradl AI is made up of **nodes**. Each node represents an action your documents go through — like model predictions, validation checks, or human review. Here’s a typical Workflow: 1. **Document upload**\ Upload documents via the API, dashboard, or integrations. 2. **Prediction**\ Run your trained extraction model to process the document. 3. **Validation**\ Apply field-level validators or confidence thresholds. 4. **Manual review**\ If validation fails, send the document to a human reviewer using Cradl AI’s built-in review UI. 5. **Export**\ Once validated, send the structured data to your target system via webhooks or direct integrations. *** # Welcome to Cradl AI Source: https://docs.cradl.ai/index Build AI agents to automate internal document workflows - fast, safe and without writing a single line of code. **Cradl AI** is a no-code platform to build and deploy AI agents that autonomously process complex documents, freeing your team from repetitive manual tasks. ## How it works Cradl AI lets you build production-grade AI agents for internal document processes in minutes. ## Core concepts Learn more about the core concepts of building AI agents with Cradl AI. Build customized AI models to parse structured information from any document. Route low-confidence predictions to a dedicated human review UI. Easily connect your AI Agent to your existing workflows via APIs, email, or automation tools Monitor and stay in control of your automation with a dedicated dashboard. ## Who Cradl AI is (and isn't) for 1. Cradl AI is designed for businesses of all sizes - from solo entrepreneurs and freelancers to small businesses and teams within large, public companies. While most of our customers work at companies with 1 to 500 employees, we also support Fortune 500 enterprises with complex procurement processes and strict compliance requirements. 2. Cradl AI is built for users of all technical backgrounds. Our mission is to provide the most intuitive and user-friendly document automation platform for **citizen developers** — so you don’t need to know anything about JSON, APIs, or LLMs to get value from Cradl AI. If you’re more technically inclined, you’ll find plenty of flexibility too. We support custom code, webhooks, HTTP requests, and more - giving you the control you need without unnecessary complexity. However, if you’re looking for an open-source tool to self-host and manage entirely through code, Cradl AI probably isn’t the best fit. # Mailhook Source: https://docs.cradl.ai/integrations/email Trigger your Cradl AI agent by emailing documents to a dedicated address. ## Introduction Use Mailhooks to send documents to your Cradl AI agent by email. Each agent gets a unique email address; any supported attachments sent to that address are processed automatically. ## Before you begin * Have a Cradl AI agent ready to receive documents. * Prepare a test document to email as an attachment. * (Optional) Decide whether to restrict allowed senders for the trigger. ## Set up your Mailhook Follow these steps to create the email trigger and test it with a sample document. In Cradl AI, open your agent and add a new trigger. Choose **Email** from the list of integrations. Copy the generated email address (for example, `12345678910@agent.cradl.ai`). In the trigger settings, you can limit which email addresses are allowed to send documents. This helps prevent unwanted submissions. Re-create the mailhook trigger or adjust the allowed senders list if the address is shared more broadly than intended. Email the mailhook address with one or more supported attachments. Example: ``` To: 12345678910@agent.cradl.ai Subject: Invoice for July Attachments: invoice-july.pdf ``` Supported attachments: PDFs, PNGs, JPEGs, WEBPs, and TIFFs. Return to your agent and confirm the document run completed successfully. When processing finishes, the extracted data is available to any configured exports. Your agent now receives documents sent to the mailhook address and processes them automatically. ## Troubleshooting * No attachments: emails without attachments are ignored. * Unsupported files: ensure attachments are a supported type and not password-protected. * Not receiving documents: double-check the mailhook address and any allowed-sender restrictions. * Forwarded messages: make sure your email client keeps attachments when forwarding (Gmail/Outlook work fine). ## FAQ Yes. Forwarding works as long as attachments are preserved by your email client. PDFs, PNGs, JPEGs, WEBPs, and TIFFs. The email is ignored and no parsing occurs. # Excel Source: https://docs.cradl.ai/integrations/excel Export extracted data from your Cradl AI agent to Microsoft Excel in OneDrive — either to separate workbooks or into a single workbook table. ## Introduction In this guide, we’ll go through the steps required to export data from a Cradl AI agent to a Microsoft Excel workbook. ## Before you begin * Ensure that you have a Microsoft 365 work or school account with access to **OneDrive**. * Prepare a test document to upload when you run the flow. Personal Microsoft accounts (e.g., @outlook.com, @hotmail.com) are not supported. ## Set up the Excel export Follow the steps below to set up your Cradl AI agent to export data to a Microsoft Excel workbook in OneDrive. In Cradl AI, open your agent and add a new export. Choose **Microsoft Excel** from the list of integrations. Click **Connect** and sign in with your Microsoft account. Approve the requested permissions. When connected, the panel shows **Connected to Excel**. Select one of the following export options: Choose this option if you want data from each processed document to be exported to a new workbook. Choose this option if you want data from each processed document to be appended as rows to an existing workbook. #### Exporting to separate workbooks Use the **Folder** picker to select where each generated workbook should be saved in OneDrive. If you're unable to find the folder you need, it is probably a [permissions](#permissions) issue. Process a test document with your agent to verify that the export works. A new workbook should appear in the selected OneDrive folder. #### Exporting to a single workbook Ensure that you have an Excel workbook in OneDrive with a table containing the columns you want to populate. To create a new table in a workbook, click **Insert → Table**. In Cradl AI, open your agent and the Excel configuration panel. In **Workbook**, search for the name of the workbook you created and select it. If you're unable to find the workbook you created, it is probably a [permissions](#permissions) issue. In **Table**, pick the Excel table inside that workbook (for example, `Table1`). If no tables are found, ensure that you have selected the right workbook and that it has a table in it. Under **Map fields**, choose which extracted field should populate each table column in your workbook (for example, we can map `Merchant` → `Vendor`, `Total Amount` → `Total Amount`). Any unmapped column remains empty. When you're done, it should look something like this: Process a test document with your agent to verify that the export works. Your agent is now ready to export data to Microsoft Excel! ## Permissions Cradl AI connects to Microsoft using your Microsoft 365 work or school account. By default, we can only search for and write to files that you own in your OneDrive (“My files”). Files that live in SharePoint/Teams or are owned by other people are different: even if you can open them in Microsoft 365, they won’t appear in our picker unless you allow broader access. If you can’t find a file or the list is empty, you have two options: 1. Copy or move the workbook (or the target folder) into your own OneDrive “My files”, then refresh the picker. 2. Click **Request additional access** in the orange message. This lets Cradl AI include shared locations you already have access to (for example, a SharePoint site or shared directory). It may require admin approval. #### Requesting elevated permissions When you choose option 2, click **Request additional access**. Cradl AI asks for permission to look beyond your personal OneDrive and include shared locations you already have access to (for example, a SharePoint site or shared directory). This doesn’t give Cradl access to anything you can’t already open; it only lets the file picker find those files. Some organizations require an administrator to approve this change. Follow the sign‑in prompt and accept the request for additional access so we can search those shared locations. If you see an admin‑approval message, contact your Microsoft 365 admin. #### For administrators In the Microsoft Entra admin center, go to **Enterprise applications**, select the Cradl AI app, open **Permissions**, and choose **Grant admin consent** for the app. You can also approve directly on the consent screen by selecting “Consent on behalf of your organization”. You still need edit rights on the target folder or workbook to export. Read‑only files won’t work. After permissions are changed or consent is granted, refresh the picker and try searching again. ## Troubleshooting * Separate workbooks: If no files appear, confirm a **Folder** is selected and the export is enabled. * Single workbook: Ensure the chosen workbook exists, contains at least one Excel table, and that the correct **Table** is selected. Reopen the workbook to create a table if needed and refresh the list in Cradl AI. * Field mapping: If values don’t show up, verify that at least one table column has a mapped extracted field. # Google Drive Source: https://docs.cradl.ai/integrations/googledrive Use Google Drive as a trigger to send new files to your Cradl AI agent via Zapier. ## Introduction Automatically send new files added to a Google Drive folder to your Cradl AI agent using Zapier. This guide walks you through creating a Zap with Google Drive as the trigger and Cradl AI as the action that extracts data from each file. ## Before you begin * A [Zapier](https://www.zapier.com) account * A Google Drive folder with a sample file to test * A Cradl AI agent to process documents ## Connecting your Cradl AI agent with Google Drive Follow the steps below to create a Zap that watches a Drive folder and processes new files in Cradl AI. In Cradl AI, open your agent and add a new trigger. Choose **Zapier** from the list of integrations. Copy the **Client ID** and **Client Secret** from the integration panel. Copy Client ID and Client Secret from the Cradl AI integration panel In Zapier, click **Create Zap**. For the **Trigger**, select **Google Drive** and choose an event: * **New File in Folder**. Fires when a file is added to a specific folder. *(Recommended)* * **New File**. Fires when a file is added anywhere you specify. *(Alternative)* Connect your Google account, then specify **Drive** and **Folder**. Test the trigger and make sure Zapier finds a sample file. Add an action step and select **Cradl AI**. For **Action Event**, choose **Extract Data From Document**. Select Cradl AI as the action app in Zapier Choose Extract Data From Document action in Zapier Under **Account**, click **Connect a new account** and paste the **Client ID** and **Client Secret** from Step 1. Click **Continue**. Configure the Cradl AI action by specifying **Agent**, **Document** and **Filename** *(optional)*. Configure the Cradl AI action in Zapier To limit to specific file types, add a Zapier **Filter** step between the trigger and action to allow only PDF, PNG, or JPG (or whichever formats your agent supports). Run a test for the Cradl AI action. Confirm that the action succeeds and that you can see extracted fields in the output. Then **Publish** your Zap to start processing new files dropped into the selected Google Drive folder. Your Zap now sends new Google Drive files to your Cradl AI agent automatically. ## Tips * Use a dedicated “inbox” folder in Drive for documents you want processed, and move or archive files after processing to avoid re‑triggers. * If the Drive trigger doesn’t include the expected file object, switch to the **New File in Folder** event and ensure the file is added after the Zap is turned on. * For larger files, allow extra time for Google Drive and Zapier to make the file available to downstream steps. ## Troubleshooting * Cradl AI action fails: Reconnect the **Cradl AI** account in Zapier using fresh credentials from the Cradl AI integration panel, then re‑test the step. * No file in the action: In the Cradl AI step, ensure **Document** is mapped to the Google Drive `File` output (not a URL or name field). * Zap doesn’t trigger: Verify the watched folder is correct and that the file was added after the Zap was turned on. For Shared Drives, enable Team Drives in the trigger setup. # Google Sheets Source: https://docs.cradl.ai/integrations/googlesheets The Google Sheets integration in Cradl AI automatically turns your extracted document data into organized, shareable spreadsheets. This integration is coming soon. # Make Source: https://docs.cradl.ai/integrations/make Production-ready document data extraction for Make. ## Introduction Use Cradl AI’s Webhook export to send extracted data to a Make scenario. You can also fetch and download the original file inside Make. A native Make integration is coming soon. Until then, connect via **Webhook**. ## Before you begin * A Make account with permission to create scenarios * A Cradl AI agent with at least one test document * API client credentials (`client_id`, `client_secret`) from your [workspace settings](https://rc.app.cradl.ai/settings/workspace) ## Connect Cradl AI to Make Create a Custom webhook in Make and point your agent’s Webhook export to it. In Make, create a new scenario and add the **Webhooks** app with the **Custom webhook** trigger. Click **Add** to create a new webhook, then copy the generated **Webhook URL**. Click **Run once** so the scenario is listening for a request. In your agent, add a new export and choose **Webhook**. Paste the Make **Webhook URL** into **URL** and set **HTTP Method** to **POST**. Click **Test webhook** and verify that the Make scenario receives a request (2xx). The captured request establishes the webhook’s data structure in Make. ## Payload format The Webhook body includes extracted fields and context such as `context.documentId`. See the [Webhook → Payload format](./webhook) for a full example. ## Download the file in Make (optional) Webhook payloads don’t include the original file. The steps below show how to get an access token, fetch the document metadata to obtain `fileUrl`, and then download the file. Add an **HTTP > Make a request** module and configure it to request an OAuth token. | Parameter | Value | | :- | :- | | **Method** | POST | | **URL** | `https://auth.cradl.ai/oauth2/token` | | **Body type** | `x-www-form-urlencoded` | | **Body** | See table below | | Field | Value | | :- | :- | | client\_id | `your client id here` | | client\_secret | `your client secret here` | | grant\_type | `client_credentials` | | audience | `https://api.cradl.ai/v1` | Save `access_token` from the JSON response. Add another **HTTP > Make a request** module to fetch document metadata using the `documentId` from the Webhooks module output. | Parameter | Value | | :- | :- | | **Method** | GET | | **URL** | `https://api.cradl.ai/v1/documents/` | | **Headers** | Add `Authorization: Bearer ` | The response includes `fileUrl`. Add an **HTTP > Get a file** module (or use **Make a request** with file download enabled) to download the original file. | Parameter | Value | | :- | :- | | **URL** | `{{fileUrl from previous step}}` | | **Headers** | `Authorization: Bearer ` | The module returns the file as binary data, which you can pass to storage apps like Google Drive, OneDrive, or S3. ## Best practices * Start the scenario in **Run once** mode when testing so Make captures the webhook sample and builds the data structure * Return a fast **2xx** from your first steps; branch long-running work downstream * Store secrets in Make’s secure fields (e.g., module fields or variables) rather than hardcoding ## Troubleshooting * No requests arriving in Make: verify the export URL and click **Test webhook** in Cradl AI; ensure the scenario is in **Run once** or scheduled * 401 from token or API calls: check `client_id`/`client_secret`, `grant_type=client_credentials`, and `audience=https://api.cradl.ai/v1` * Missing fields when mapping: run the scenario once to recapture the webhook payload and refresh the data structure * Cannot download file: include the `Authorization: Bearer ` header when requesting `fileUrl` # n8n Source: https://docs.cradl.ai/integrations/n8n Production-ready document data extraction for n8n. ## Before you begin * An n8n instance (Cloud or self‑hosted) with permission to create workflows * A Cradl AI agent with at least one test document ## Quickstart Follow these steps to set up the Cradl AI node in n8n. Add a new node to your n8n workflow and search for "Cradl AI". n8n Webhook node overview Open the Cradl AI node in n8n and click **Set up credential**. In your Cradl AI agent, add a new trigger and choose **n8n**. Copy the *Client ID* and *Client Secret* from the n8n trigger panel, then paste them into n8n. n8n Cradl AI trigger panel Cradl AI credentials in n8n You can also add an n8n export to your Cradl AI agent at this stage. This is optional. If you skip it, the export is created automatically the first time you run the n8n workflow. Once your credentials are set up, your Cradl AI agents should appear under **Agent Name or ID**. Select the agent you want to use. In the **Document Binary Data** field, specify the file you want Cradl AI to read. This is typically a binary PDF or image file. Enter the name of the binary variable in this field, as shown below: Cradl AI agent setup ## Triggering n8n workflows from Cradl AI Use the Cradl AI trigger to start an n8n workflow when results are available in Cradl AI. This is also useful for asynchronous workflows, so you do not need to wait for results in the middle of a workflow. n8n Cradl AI trigger Follow the same steps as in [Quickstart](#quickstart) to configure the trigger node. ## Response format The n8n node returns extracted fields and context, such as `context.documentId`. See [Webhook → Payload format](./webhook) for a full example. ## Best practices * Keep node names stable (e.g., `Webhook`, `HTTP Request`) if you use expressions that reference them * Return a fast **2xx** response from the Webhook node; perform longer work asynchronously downstream * Store secrets using n8n Credentials instead of hardcoding values ## Troubleshooting * No requests arriving in n8n: verify that the workflow is active and that the node is configured correctly * 401 from token or API calls: check `client_id`/`client_secret`, `grant_type=client_credentials`, and `audience=https://api.cradl.ai/v1` * Expression resolves to `undefined`: confirm node names match your expressions and inspect the incoming JSON with the Expression Editor to validate the path (e.g., `body.context.documentId`) * Cannot download file: include the `Authorization: Bearer ` header when requesting `fileUrl` # OneDrive Source: https://docs.cradl.ai/integrations/onedrive Trigger your Cradl AI agent from OneDrive using Power Automate. ## Introduction Automatically process new files added to a OneDrive folder by connecting OneDrive to your Cradl AI agent through Power Automate. This guide sets up a flow that watches a folder in OneDrive for Business and sends each new file to Cradl AI for extraction. ## Before you begin * A Microsoft 365 work or school account with access to OneDrive for Business * Access to Power Automate and the Cradl AI connector in your tenant * A Cradl AI agent ready to process documents * A test file in your OneDrive to validate the flow ## Connect OneDrive to your Cradl AI agent Follow the steps below to create a Power Automate flow that triggers on new files in OneDrive and forwards them to Cradl AI. In Cradl AI, open your agent and add a new trigger. Choose **Power Automate** and select **Import from Power Automate**. Copy the **Client Credentials** value from the configuration panel — you’ll paste this when creating the connection in Power Automate. Copy client credentials from the Cradl AI integration panel In Power Automate, click **New flow** and choose **Automated cloud flow**. Name the flow (for example, "Send invoices to Cradl AI"). Create a new automated cloud flow in Power Automate For the trigger, select **OneDrive for Business → When a file is created**. Click **Folder** and pick the folder to watch. Optionally, enable **Infer Content Type** to help downstream actions. Tip: If you want to limit which files are processed (for example, only PDFs), you can add a **Condition** step later to check the file extension before calling Cradl AI. Add the action **OneDrive for Business → Get file content**. For **File**, use the `Identifier` output from the **When a file is created**-trigger. This provides the binary content needed by Cradl AI. Add the action **Cradl AI → Extract Data From Document**. If prompted to create a connection, paste the **Client Credentials** from Step 1. Paste client credentials to create the Cradl AI connection To configure the action, set **Agent** to the Cradl AI agent that should process the document and set **Document** to the `File Content` from the previous OneDrive step. Optionally, set **Filename** to the file’s original name (for example, use the `Name` from the trigger) so the agent receives a proper file name. Select the Extract Data From Document action in Power Automate Save the flow. Add a test file to the selected OneDrive folder. In Power Automate, open **Run history** and confirm the flow succeeds. Then open your Cradl AI agent and verify that the document has been sent to you agent. Your flow now sends new OneDrive files to your Cradl AI agent automatically. ## Best practices * Use a dedicated “inbox” folder in OneDrive for files to be processed. Move or archive files after successful processing to avoid re‑processing. * To restrict by type, add a **Condition** after the trigger that checks `Name` ends with `.pdf`, `.png`, or `.jpg` (or the formats your agent supports). * After extraction, add steps to route results where you need them — for example, create a row in Excel, post to a webhook, or update a business system. ## Troubleshooting * Cradl AI action fails with 401/403: Reopen the integration in Cradl AI, copy fresh client credentials, and update the Power Automate connection. * No file content in action: Ensure you added **Get file content** and mapped its `File Content` to the **Document** field. * Flow doesn’t trigger: Verify the correct OneDrive folder is selected and that the test file was added after the flow was turned on. For shared libraries or SharePoint-backed folders, confirm your account has access and the trigger supports that location. # Power Automate Source: https://docs.cradl.ai/integrations/powerautomate Production-ready document data extraction for Power Automate. ## Before you begin * Verify the **Cradl AI for Power Automate** connector is available in your tenant (through a premium license or custom connector deployment). * Prepare a test document to upload when you run the flow. ## Setting up your Power Automate flow Follow these steps to set up your Power Automate flow with Cradl AI. In Cradl AI, open your agent and add a new trigger. Choose **Power Automate**, then select **Import from Power Automate**. Copy the **Client Credentials** value from the configuration panel. Copy client credentials from the Cradl AI Integration panel In your Power Automate flow, add the **Extract data from document** action. Adding the Extract data from document action in Power Automate In the **Client Credentials** field, paste the value you copied in Step 1. Pasting client credentials to create the Cradl AI connection Open the **Extract data from document** action settings and choose the Cradl AI **Agent** that should process the file. For **Document**, select the binary file content from an earlier step, such as the `File Content` output from **Get file content (OneDrive)** or a file supplied by a manual trigger. For **Export action**, choose the Power Automate export you added to your Cradl AI agent. Optionally, set **Title** to pass along the file name and extension. Configuring the Extract data from document action with Agent and Document inputs After the action runs, the fields from your Cradl AI agent are available as dynamic content in Power Automate. Select these values in later actions the same way you use other outputs from the flow. Selecting extracted Cradl AI fields as dynamic content in Power Automate Your flow now sends documents from Power Automate to your Cradl AI agent and makes the extracted fields available to later actions. ## Troubleshooting * Run the flow manually with your sample document and review the **Run history** entry. Expand the **Extract data from document** action and confirm its status code is `200`. * If Power Automate reports `401 Unauthorized` or `404 Forbidden`, reopen the Cradl AI integration, copy fresh client credentials, and update the connection. * For other errors, make sure the file format is supported, the document is not encrypted, and your Cradl AI usage limits have not been exceeded. The message under **Outputs → Body** provides details when validation fails. # Webhook Source: https://docs.cradl.ai/integrations/webhook Use webhooks to export data from Cradl AI. ## Introduction Webhooks let you send structured results from your Cradl AI agent to any HTTP endpoint. Use this to push data into your own service or systems like n8n, Make, or custom APIs. ## Before you begin * Have an HTTPS endpoint that accepts JSON requests * Optionally prepare a request inspector (e.g., your own dev endpoint) to verify the payload * Upload a test document to your agent so there’s data to send ## Configure your webhook The steps below add the webhook export in Cradl AI and verify that your endpoint receives the payload. In your agent, add a new export and select **Webhook**. You’ll see fields for the **URL**, **HTTP Method**, and optional **HTTP Headers**. Webhook settings in Cradl AI Enter your endpoint in **URL** and choose the **HTTP Method** your service expects (POST is common). Cradl AI sends a JSON body with the extracted data and run context. Add custom **HTTP Headers** for example for authentication or routing. Click **Test webhook** to send a sample payload to your URL. Confirm that your endpoint receives the request and returns a **2xx** response. ## Payload format Your endpoint receives a JSON body similar to the example below. The `output` section contains field values organized by group and field IDs. Each field entry includes the recognized `value`, original `rawValue`, `confidence`, and `page`. The `context` section includes references like `runId` and `documentId`. ```json theme={null} { "output": { "n2379dpkjj": [ { "d6xz8w5wnr": { "rawValue": "The Meat", "value": "The Meat", "confidence": 0.926, "page": 0, "name": "Merchant" }, "2i1sspmmxw": { "rawValue": "2017-05-23", "value": "2017-05-23", "confidence": 0.945, "page": 0, "name": "Purchase Date" }, "a8b40ix8k9": { "rawValue": "138,00", "value": "138", "confidence": 0.982, "page": 0, "name": "Total Amount" }, "s5otx28oyk": { "rawValue": "SEK", "value": "SEK", "confidence": 0.244, "page": 0, "name": "Currency" } } ] }, "context": { "runId": "cradl:run:eee4d54340cfe1071a98735234a66755", "documentId": "cradl:document:349e99a0d98046db8219b70393a3289f" } } ``` Field and group IDs are stable identifiers. The human-friendly field name is available under `name` for convenience. Prefer the normalized `value` when mapping, and fall back to `rawValue` if needed. ## Downloading the file (optional) The webhook payload does not include the original file. If you need it, please refer to the [API reference](/api-reference/introduction). ## Verifying signatures Each webhook request is signed with an HMAC SHA‑256 signature so you can verify authenticity and protect against replay attacks. The following headers are automatically added by Cradl AI: * `X-Cradl-Timestamp` — Unix timestamp (seconds) when the request was created * `X-Cradl-Nonce` — A random UUIDv4 (hex, no dashes) * `X-Cradl-Signature` — Hex-encoded HMAC SHA‑256 over the message described below #### How the signature is computed Given a shared signing secret, the signature is computed on the concatenation of these exact byte sequences: 1. HTTP method (e.g., `POST`), as bytes 2. Full request URL (including query string), as bytes 3. All HTTP headers except `X-Cradl-Signature`, sorted by header name and concatenated with no delimiter as `key:value` pairs, then concatenated together (still no delimiter). 4. Raw request body bytes (the JSON payload) Reject requests where `X-Cradl-Timestamp` is too old (e.g., >5 minutes) or where a previously seen `X-Cradl-Nonce` is reused #### Examples The code examples below have been generated by an AI. Use them as a starting point and **verify correctness** before using in production. ```python Python theme={null} import hmac, hashlib from flask import request def verify_cradl_webhook(signing_secret: str) -> bool: provided = request.headers.get('X-Cradl-Signature', '') method = request.method.upper() url = request.url # full URL including query headers = [(k, v) for k, v in request.headers.items() if k.lower() != 'x-cradl-signature'] headers.sort(key=lambda kv: kv[0]) header_blob = ''.join(f'{k}:{v}' for k, v in headers) body = request.get_data(cache=False, as_text=False) # raw bytes message = method.encode() + url.encode() + header_blob.encode() + body expected = hmac.new(signing_secret.encode(), message, hashlib.sha256).hexdigest() return hmac.compare_digest(provided, expected) ``` ```javascript JavaScript theme={null} import crypto from 'crypto'; import express from 'express'; const app = express(); // Capture raw body so we can verify exactly what was sent app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => { const signingSecret = process.env.CRADL_SIGNING_SECRET; const provided = req.get('X-Cradl-Signature') || ''; // Full URL exactly as received const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`; // Rebuild headers from rawHeaders to preserve original casing const pairs = []; for (let i = 0; i < req.rawHeaders.length; i += 2) { const name = req.rawHeaders[i]; const value = req.rawHeaders[i + 1]; if (name.toLowerCase() === 'x-cradl-signature') continue; pairs.push([name, value]); } pairs.sort((a, b) => a[0].localeCompare(b[0])); const headerBlob = pairs.map(([k, v]) => `${k}:${v}`).join(''); const method = req.method.toUpperCase(); const chunks = [method, url, headerBlob].map(String).map(Buffer.from); chunks.push(Buffer.from(req.body)); // raw bytes const message = Buffer.concat(chunks); const expectedHex = crypto.createHmac('sha256', signingSecret) .update(message) .digest('hex'); // timing‑safe compare of hex strings const toHexBuf = (s) => Buffer.from(String(s), 'hex'); let ok = false; try { const a = toHexBuf(provided); const b = toHexBuf(expectedHex); ok = a.length === b.length && crypto.timingSafeEqual(a, b); } catch (_) { ok = false; } res.sendStatus(ok ? 204 : 401); }); ``` ```go Go theme={null} package main import ( "crypto/hmac" "crypto/sha256" "crypto/subtle" "encoding/hex" "fmt" "io" "net/http" "os" "sort" "strings" ) func verifyCradlWebhook(r *http.Request, secret string, body []byte) bool { provided := r.Header.Get("X-Cradl-Signature") scheme := "http" if r.TLS != nil { scheme = "https" } if xf := r.Header.Get("X-Forwarded-Proto"); xf != "" { scheme = xf } url := fmt.Sprintf("%s://%s%s", scheme, r.Host, r.URL.RequestURI()) names := make([]string, 0, len(r.Header)) for k := range r.Header { if strings.EqualFold(k, "X-Cradl-Signature") { continue } names = append(names, k) } sort.Slice(names, func(i, j int) bool { return strings.ToLower(names[i]) < strings.ToLower(names[j]) }) var headerBlob strings.Builder for _, n := range names { headerBlob.WriteString(n) headerBlob.WriteString(":") headerBlob.WriteString(strings.Join(r.Header[n], ",")) } msg := []byte(r.Method) msg = append(msg, []byte(url)...) msg = append(msg, []byte(headerBlob.String())...) msg = append(msg, body...) mac := hmac.New(sha256.New, []byte(secret)) mac.Write(msg) expected := mac.Sum(nil) prov, err := hex.DecodeString(provided) if err != nil { return false } return len(prov) == len(expected) && subtle.ConstantTimeCompare(prov, expected) == 1 } func handler(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) ok := verifyCradlWebhook(r, os.Getenv("CRADL_SIGNING_SECRET"), body) if ok { w.WriteHeader(http.StatusNoContent) } else { w.WriteHeader(http.StatusUnauthorized) } } ``` ```java Java theme={null} import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import jakarta.servlet.http.HttpServletRequest; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.*; @RestController public class WebhookController { private final String signingSecret = System.getenv("CRADL_SIGNING_SECRET"); @PostMapping("/webhook") public ResponseEntity webhook(HttpServletRequest req, @RequestBody byte[] body) throws Exception { String provided = Optional.ofNullable(req.getHeader("X-Cradl-Signature")).orElse(""); String method = req.getMethod(); String url = req.getRequestURL().toString(); if (req.getQueryString() != null) url += "?" + req.getQueryString(); List names = Collections.list(req.getHeaderNames()); names.removeIf(n -> n.equalsIgnoreCase("X-Cradl-Signature")); names.sort(String.CASE_INSENSITIVE_ORDER); StringBuilder headerBlob = new StringBuilder(); for (String name : names) { List values = Collections.list(req.getHeaders(name)); headerBlob.append(name).append(":").append(String.join(",", values)); } byte[] message = concat( method.getBytes(StandardCharsets.UTF_8), url.getBytes(StandardCharsets.UTF_8), headerBlob.toString().getBytes(StandardCharsets.UTF_8), body ); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(signingSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); byte[] expected = mac.doFinal(message); String expectedHex = bytesToHex(expected); boolean ok = provided.length() == expectedHex.length() && MessageDigest.isEqual(provided.toLowerCase(Locale.ROOT).getBytes(StandardCharsets.UTF_8), expectedHex.getBytes(StandardCharsets.UTF_8)); return ok ? ResponseEntity.noContent().build() : ResponseEntity.status(401).build(); } private static byte[] concat(byte[]... arrays) { int len = 0; for (byte[] a : arrays) len += a.length; byte[] out = new byte[len]; int pos = 0; for (byte[] a : arrays) { System.arraycopy(a, 0, out, pos, a.length); pos += a.length; } return out; } private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 2); for (byte b : bytes) sb.append(String.format("%02x", b)); return sb.toString(); } } ``` ```kotlin Kotlin theme={null} import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import jakarta.servlet.http.HttpServletRequest import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec import java.nio.charset.StandardCharsets import java.security.MessageDigest import java.util.* @RestController class WebhookController { private val signingSecret: String = System.getenv("CRADL_SIGNING_SECRET") ?: "" @PostMapping("/webhook") fun webhook(req: HttpServletRequest, @RequestBody body: ByteArray): ResponseEntity { val provided = req.getHeader("X-Cradl-Signature") ?: "" val method = req.method var url = req.requestURL.toString() req.queryString?.let { url += "?" + it } val names = Collections.list(req.headerNames) .filterNot { it.equals("X-Cradl-Signature", ignoreCase = true) } .sortedBy { it.lowercase() } val headerBlob = buildString { for (name in names) { val value = Collections.list(req.getHeaders(name)).joinToString(",") append(name).append(":").append(value) } } val message = method.toByteArray() + url.toByteArray() + headerBlob.toByteArray() + body val mac = Mac.getInstance("HmacSHA256") mac.init(SecretKeySpec(signingSecret.toByteArray(), "HmacSHA256")) val expectedHex = mac.doFinal(message).joinToString("") { "%02x".format(it) } val ok = provided.length == expectedHex.length && MessageDigest.isEqual(provided.lowercase().toByteArray(), expectedHex.toByteArray()) return if (ok) ResponseEntity.noContent().build() else ResponseEntity.status(401).build() } } ``` ```csharp C# theme={null} using System.Security.Cryptography; using System.Text; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapPost("/webhook", async (HttpRequest req) => { var provided = req.Headers["X-Cradl-Signature"].ToString() ?? string.Empty; req.EnableBuffering(); using var ms = new MemoryStream(); await req.Body.CopyToAsync(ms); var body = ms.ToArray(); req.Body.Position = 0; var method = req.Method.ToUpperInvariant(); var url = $"{req.Scheme}://{req.Host}{req.Path}{req.QueryString}"; var names = req.Headers .Where(h => !string.Equals(h.Key, "X-Cradl-Signature", StringComparison.OrdinalIgnoreCase)) .Select(h => h.Key) .OrderBy(k => k, StringComparer.OrdinalIgnoreCase) .ToList(); var headerBlob = new StringBuilder(); foreach (var name in names) { var value = string.Join(",", req.Headers[name].ToArray()); headerBlob.Append(name).Append(":").Append(value); } byte[] message = Combine( Encoding.UTF8.GetBytes(method), Encoding.UTF8.GetBytes(url), Encoding.UTF8.GetBytes(headerBlob.ToString()), body ); var secret = Environment.GetEnvironmentVariable("CRADL_SIGNING_SECRET") ?? string.Empty; using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var expectedHex = Convert.ToHexString(hmac.ComputeHash(message)).ToLowerInvariant(); var ok = CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(provided.ToLowerInvariant()), Encoding.UTF8.GetBytes(expectedHex) ); return Results.StatusCode(ok ? 204 : 401); }); app.Run(); static byte[] Combine(params byte[][] arrays) { var len = arrays.Sum(a => a.Length); var rv = new byte[len]; var pos = 0; foreach (var a in arrays) { Buffer.BlockCopy(a, 0, rv, pos, a.Length); pos += a.Length; } return rv; } ``` ## Best practices * Return a fast **2xx** response after receiving the payload; perform longer processing asynchronously if possible * Use a shared secret or token in headers to authenticate requests from Cradl AI * Log the entire body during development to validate field mappings ## Troubleshooting * No requests arriving: Verify the **URL** is publicly reachable and click **Test webhook** * 401/403 from your service: Check header names/values and tokens * Missing fields: Upload and validate a fresh document, then test again to refresh available data * Cannot download file: Include the `Authorization: Bearer ` header when requesting `fileUrl` * If you're using a proxy or gateway, ensure it doesn't modify headers or the request body # Zapier Source: https://docs.cradl.ai/integrations/zapier Production-ready document data extraction for Zapier. ## Before you begin * Ensure that you have an active [Zapier](https://www.zapier.com) account * Prepare a test document to upload when you run your Zap. ## Setting up your Zap Follow these steps to set up your Zapier workflow with Cradl AI. In Cradl AI, open your agent and add a new trigger and export. Choose **Zapier** from the list of integrations. Copy the **Client ID** and **Client Secret** from the Zapier integration panel. Copy Client ID and Client Secret from the Cradl AI integration panel Add a new action step in your Zap and select **Cradl AI** as the app. For **Action Event**, choose **Extract Data From Document**. Add a new action step in Zapier Add a new action step in Zapier Under **Account**, click **Connect a new account** and paste the **Client ID** and **Client Secret** you copied in Step 1, then click **Continue**. Choose the Cradl AI **Agent** that should process the file. For **Document** (or **File**), select the file from your trigger step. For **Action**, choose the Zapier export you added to your Cradl AI agent. Optionally, set **Filename** to pass the original file name to your agent. Configure the Cradl AI action in Zapier **Pro tip**: If you need to pass additional values to your agent, add them as *Variables*. They will be visible to other actions in your agent. Run a test for the action and confirm a successful response and parsed output. Then **Publish** your Zap. Your Zap now sends documents from Zapier to your Cradl AI agent. ## Troubleshooting * If Zapier reports `401 Unauthorized` or `403 Forbidden`, reconnect the **Cradl AI** account in Zapier using fresh credentials. * If no output appears, make sure the file type is supported and not encrypted. Try again with a small sample document. * If downstream steps can’t see fields, re-test the Cradl AI action in Zapier to refresh available output fields. # Description of Service Source: https://docs.cradl.ai/legal/dos Features, functionality, and scope of the services provided. The Service is a cloud-based service for automating document workflows using machine-learning models to extract key information from scanned, photographed or digital documents. The Service is provided as a web application and as a REST API. As part of the processing, the Service uses machine learning models to extract information from unstructured documents. All predictions made by the machine learning models shall be considered as suggestions, and Licensee is responsible for deciding whether to trust the suggested result of the scanning and use the information from the scanning as suggested. ## **1. DATA FLOW** The following steps describe the flow of document data when using the Service: 1. Licensee posts a document to the Service. A retention policy is used by Cradl AI to determine how long the document can be stored and used for further training. The document is stored securely on Cradl AI’s servers which are hosted on Amazon Web Services in Ireland. 2. If the document is processed and extracted information is returned as a JSON response. Feedback posted to the Service for the purpose of improving the machine learning models is stored securely with the document. 3. Licensee may at any time delete one or all of the documents posted to the Service by using the API. 4. If the document is submitted to one of Cradl AI’s workflow endpoints as part of the processing, the document will become available in the validation interface. ## **2. CONTINUOUS TRAINING** Documents posted to the Service may be used by Cradl AI for training Cradl AI’s machine learning models. Cradl AI supports two training modes: #### Model fine-tuning The following steps outline the current process for manual fine-tuning. 1. Automatic anomaly checks are run on the collected data to rule out data points that can be detrimental to the training process. 2. Random checks of the data may be conducted by Cradl AI’s employees to rule out data points that can be detrimental to the training process. After the pre-processing, the following training routine is executed: 1. Cradl AI’s machine learning models are prepared and configured for training by Cradl AI. 2. The trained models are evaluated, tested and deployed to production if the new model is significantly better (in terms of statistical significance) than the previous model. #### In-context learning The following steps outline the current process for manual fine-tuning. 1. Automatic anomaly checks are run on the collected data to rule out data points that can be detrimental to the training process. 2. Random checks of the data may be conducted by Cradl AI’s employees to rule out data points that can be detrimental to the training process. 3. Training data is automatically used to improve the Licensee's predictions, but without altering the underlying machine learning model. ## **3. FEATURES** A complete description of features and capabilities of the Service is available in Cradl AI’s [documentation](/). # Data Processing Agreement Source: https://docs.cradl.ai/legal/dpa How personal data is collected, processed, stored, and protected in compliance with regulations. ## 1. **BACKGROUND AND PURPOSE** This Data Processing Agreement ("**DPA**") forms part of the Services Agreement between: Lucidtech ("**Data Processor**") and the Licensee (“**Data Controller**”) together referred to as the “**Parties**”. The Data Processor processes Personal Data either 1. as a data processor on behalf of the Licensee as a Data Controller; or 2. as a sub-processor on behalf of another data processor that processes Personal Data on behalf of its customers, which are Data Controllers. For the purposes of fulfilling the Services Agreement, the Processor will process certain Personal Data on behalf of the Controller. This DPA sets forth the terms and conditions pursuant to which the Processor shall process Personal Data on behalf of the Controller under the Services Agreement. ## **2. DEFINITIONS** In this DPA, the following terms shall have the meanings set out below: * "**Data Processor**" means Processor or a Sub-processor; * "**Data Protection Laws**" means EU Data Protection Laws and, to the extent applicable, the data protection or privacy laws of any other country, such as the Norwegian Personal Data Act (LOV-2018-06-15-38) and Personal Data Regulations; * "**EU Data Protection Laws**" means EU Directive 95/46/EC, as transposed into domestic legislation of each Member State and as amended, replaced or superseded from time to time, including by the GDPR and laws implementing or supplementing the GDPR; * "**GDPR**" means EU General Data Protection Regulation 2016/679; * "**Personal Data**" means any information relating to an identified or identifiable natural person (“**Data Subject**”); an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person; * "**Sub-processor**" means a third party sub-contractor engaged by the Processor which, will Process Personal Data on behalf of the Controller; and The terms "**Controller**", "**Member State**", "**Personal Data**", "**Personal Data Breach**", and "**Processing**" shall have the same meaning as in the GDPR, and their cognate terms shall be construed accordingly. In addition, definitions set out in [Terms of Service](tos.md). will also apply to words in this DPA when written in capital letters. ## **3. PROCESSING OF PERSONAL DATA** The Data Processor processes data on behalf of the Data Controller in connection with offering machine learning powered cloud services for reading and validating documents such as receipts and invoices, with continuous self-improvement capabilities as an integral part of the service. The Processing involves extracting information such as date, total amount, supplier, currency, etc. from the documents which may consist of Personal Data. This extracted information will be returned to the Data Controller in a structured format. After Processing, the documents may be stored for up to 5 years on the Data Processor’s systems in order to fulfill the Data Processor’s duties to the Data Controller. The Data Processor will process the following types of personal data on behalf of the Data Controller: * Name, IP-address, contact information, occupation, purchase and other personal information that may appear on documents posted to the Service. The Personal Data is connected to the following categories of Data Subjects: * The employees or customers of the Data Controller. The Data Processor shall only process Personal Data for the following purposes: * To supply machine learning powered self-improving document interpretation cloud services to the Data Controller. * Train machine learning models to continuously improve the accuracy of the Data Controller’s AI models, but only upon the explicit request of the Data Controller. The Data Processor may not use any data provided by the Data Controller to train or improve underlying base models. The Data Processor shall not process Personal Data in any other manner than what is agreed in this DPA and on documented instructions from the Data Controller. This includes that the Data Processor is not allowed to process data for other purposes than as stated above or its own purposes or to disclose data to third parties. ## **4. THE DATA PROCESSOR’S DUTIES** When Processing Personal Data on behalf of the Data Controller, the Data Processor shall follow the routines and instructions stipulated in this DPA. Data Processor shall: 1. comply with all applicable Data Protection Laws in the Processing of Personal Data; and 2. not Process Personal Data other than on the Data Controller’s documented instructions unless Processing is required by Applicable Laws to which the Data Processor is subject, in which case Processor shall to the extent permitted by Applicable Laws inform the Controller before the relevant Processing of that Personal Data, and 3. immediately inform the Data Controller if, its opinion, an instruction infringes applicable Data Protection laws. The Data Processor is obliged to give the Data Controller access to his written technical and organizational security measures and to provide assistance so that the Data Controller can fulfill its responsibilities pursuant to the Personal Data Act and the General Data Protection Regulation. The Data Processor undertakes to only Process Personal Data in accordance with documented instructions communicated by the Data Controller, unless required to do so pursuant to the Applicable Data Protection Law. The Data Processor shall assist the Data Controller in fulfilling its legal obligations under Applicable Data Protection Law. In the event the Data Processor, according to Applicable Data Protection Law, is required to disclose Personal Data that the Data Processor Processes on behalf of the Controller, the Data Processor will inform the Data Controller thereof. The Data Processor may not in any way act on behalf of or as a representative of the Data Controller. Unless otherwise agreed or pursuant to statutory regulations, the Data Controller is entitled to access all Personal Data being processed on behalf of the Data Controller and the systems used for this purpose. The Data Processor shall provide the necessary assistance for this. The Data Processor may not, without prior written approval from the Data Controller, transfer or in any other way disclose Personal Data or any other information relating to the Processing of Personal Data to any third party. This applies with the exception of Sub-processors engaged pursuant to this DPA. The Data Processor shall not process Personal Data outside the EU/EEA, unless otherwise stated in this DPA. If the transferring of Personal Data to a country outside the EU/EEA or to an international organization outside the EU/EEA is required according to law in an EU/EEA Member State which the Data Processor is subject to or EU/EEA law, the Data Processor shall inform the Data Controller of such requirement prior to the Processing, unless the law prohibits such information from being given. ## **5. THE DATA PROCESSOR’S OPPORTUNITY TO USE SUB-PROCESSORS** The Data Processor may use Sub-processors. The Data Processor shall ensure that the Sub-processors are bound by written agreements that require them to comply with data processing obligations corresponding to those contained in this DPA. The Data Processor shall remain fully liable to the Data Controller for the performance of the Sub-processor's obligations. The following Sub-processor(s) are approved by the Data Controller: * Amazon Web Services EMEA SARL In addition, the Data Processor has the right to use other Sub-processors, but is obliged to inform the Data Controller of any intended changes concerning the addition or replacement of other processors. The information shall be given at least three months prior to the planned changes taking effect. If the Data Controller does not consent in the change, the Data Controller has the right to terminate the Agreement with three month’s notice. ## 6. TRANSFER OF PERSONAL DATA OUTSIDE THE EU/EAA The Data Processor may not process or use Sub-processors that process Personal Data outside the EU/EEA without prior written approval from the Data Controller. The Data Processor shall ensure that there is a legal basis for the Processing of Personal Data data outside the EU/EEA, or facilitate the establishment of such legal basis. ## **7. SECURITY** The Data Processor shall, in order to assist the Data Controller in fulfilling its legal obligations under Applicable Data Protection Law regarding security measures and privacy impact assessments, be obliged to take appropriate technical and organizational measures to protect the Personal Data, which is Processed. The Data Processor shall comply with any written information security requirements or policies communicated by the Data Controller from time to time, including, as appropriate, the measures referred to in Article 32(1) of the GDPR. The Data Processor shall maintain adequate security for the Personal Data appropriate to the risk of Processing. The Data Processor shall protect the Personal Data against destruction, modification, unlawful dissemination, or unlawful access. Having regard to the state of the art and the costs of implementation and taking into account the nature, scope, context and purposes of the Processing as well as the risk of varying likelihood and severity for the rights and freedoms of individuals, the Data Processor shall implement technical and organizational measures to secure the data. The Data Processor shall document routines and other measures made to comply with these requirements regarding the information system and security measures. The documentation shall be available at request by the Data Controller and the authorities. In addition to the technical and organizational measures mentioned above, the Data Processor shall implement the following measures: 1. Access control whereby access to the Personal Data is managed through a technical system for authorization control. There shall be procedures for allocating and removing authorizations. 2. User IDs and passwords used to access Personal Data shall be personal and shall not be shared. 3. Secure communications whereby external data communication connections shall be protected using technical functions ensuring that the connection is authorized and encrypted for Personal Data in transit in communication channels outside systems controlled by the Data Processor. 4. A process to ensure secure data destruction when fixed or removable storage media no longer are used for their purpose. 5. Computer equipment and removable storage media containing Personal Data at the Data Processor’s premises shall be encrypted in order to protect against unauthorized use and theft. 6. A process to ensure the confidentiality of employees who have access to the Personal Data. 7. Routines for entering into confidentiality agreements with suppliers providing repair and service of equipment used to store Personal Data. 8. Routines for supervising the service performed by suppliers at the premises of the Data Processor. Storage media containing the Personal Data shall be locked up if supervision is not possible. The Data Processor shall maintain a record of all categories of Processing activities carried out on behalf of the Data Controller. The Data Processor shall prepare and keep updated a description of its technical, organisational and physical measures to be, and maintain, compliant with the applicable Data Protection Law. ## **9. PERSONAL DATA BREACH** In case of a Personal Data Breach involving Personal Data Processed on behalf of the Data Controller, the Data Processor shall assist the Data Controller in ensuring compliance with the Data Controller’s obligations pursuant to Applicable Data Protection Law, including, but not necessarily limited to, article 33 in the GDPR. The Data Processor shall notify the Data Controller in writing without undue delay, but not later than 24 hours after becoming aware of such a Personal Data Breach. Considering the nature of the Processing performed by and the information available to the Data Processor, the Data Processor shall assist the Data Controller in fulfilling the obligations of the General Data Protection Regulation article 32 to 36. To the extent the Data Controller requires assistance from the Data Processor that exceeds the scope of this DPA, the Data Processor may offer such assistance as a separately paid service. ## **10. DOCUMENTATION AND SECURITY AUDITS** The Data Processor shall have documentation that proves that the Data Processor complies with its obligations under this DPA and the General Data Protection Regulation. The documentation shall be available for the Data Controller on request. The Data Processor shall regularly conduct security audits, and shall submit the results of the audit to the Data Controller. The Data Controller shall be entitled to conduct audits and inspections regularly, for systems etc. covered by this DPA, in accordance with the requirements of the Personal Data Act, the Personal Data Regulations and the General Data Protection Regulation. Audits may be carried out by a third party mandated by the Data Controller. The third party will be subject to confidentiality (including signing declarations of confidentiality). The audit does not include information concerning Data Processor’s trade secrets. This includes, but is not limited to product know-how, algorithms, software code, test results, processes, inventions, research projects etc. ## **11. FULFILLING THE RIGHTS OF THE DATA SUBJECTS** The Data Processor’s processing on behalf of the Data Controller is not of a nature which makes it necessary or reasonable for the Data Processor to fulfill or assist in fulfilling the Data Controller’s obligations towards Data Subjects. To the extent the Data Controller requires assistance from the Data Processor, the Data Processor will offer such assistance as a separately paid service. The Data Processor may also refuse, unless the Data Processor’s assistance is necessary in order to be able to fulfill the Data Controller’s obligations. ## **12. THE DURATION OF THE DPA AND THE PROCESSING** The DPA applies as long as the Data Processor processes and/or retains Personal Data on behalf of the Data Controller according to the Services Agreement. ## **13. TERM AND TERMINATION** The DPA may be terminated in accordance with the termination clauses in the Services Agreement. A termination of the Services Agreement also constitutes a termination of the DPA. ## **14. LIABILITY** The Parties may claim damages in respect of any direct loss as specified in the Services Agreement in relation to breaches of this DPA. The liability for damages does not extend to indirect loss, including lost profits or anticipated savings. Loss of data is considered as an indirect loss. The maximum damages that can be awarded pursuant to this DPA is limited to a sum equivalent to the maximum liability in the Services Agreement. ## **15. RETURN, DELETION AND/OR DESTRUCTION OF DATA UPON TERMINATION OF THE DPA** Upon termination of this DPA the Data Processor shall (i) cease all its Processing activities and (ii) upon the Data Controller’s choice, delete and/or return all Personal Data or copies thereof which is received on behalf of the Data Controller pursuant of this DPA. The duty to delete applies as long as Applicable Data Protection Law does not require the Personal Data to be stored. The Data Processor may, subject to prior express approval of the Data Controller, anonymize all Personal Data received from or on behalf of the Data Controller which is comprised by the DPA. The Data Processor will permanently erase all Personal Data processed under the DPA (regardless of where and how they are stored), for which the Customer is Data Controller, unless the Data Processor is required by law to store the Personal Data. # End User License Agreement Source: https://docs.cradl.ai/legal/eula The terms under which users can access and use the software or application. This End User License Agreement (hereinafter “**Agreement**”) is entered into between Lucidtech AS dba. Cradl AI (hereinafter “**Lucidtech**”, "**Cradl AI**" or “**we**”) with legal entity number 918 345 787 MVA and the user of the Lucidtech technology (hereinafter “**User**”or “**you**”), together referred to as the “**Parties**”. Lucidtech is delivering cloud-based service for automating document workflows based on machine learning technology (hereinafter, the “**Service**” or "**Cradl**"). The Service is provided as a web application and as an API through its business partners based on a separate license agreement (hereinafter “**Partners**”). The Partners’ customers and their employees or other individuals that you permit to access the Service are the Users of the Service and the licensees to this Agreement. Lucidtech grants to the User, a non-exclusive, non-transferable, on-sublicensable right to access and use the Software for the term, specific purpose and subject to the terms and conditions set out in this Agreement. By accessing or using the Service or any part thereof, you acknowledge that you have read, understood and agree to be bound by the terms of the Agreements. Please read this Agreements carefully before using the Service and revise it for updates. ## **1. INTELLECTUAL PROPERTY RIGHTS** “**Intellectual Property Rights**” means all rights in and to any copyright, trademark, trading name, design, patent, know how (trade secrets) and all other rights resulting from intellectual activity in the industrial, scientific, literary or artistic field and any application or right to apply for registration of any of these rights and any right to protect or enforce any of these rights. All Intellectual Property Rights over and in respect of the Service, are owned by Lucidtech. The User does not acquire any rights of ownership to any of Lucidtech’s APIs, machine learning, machine learning models or any other part of the Service. Nothing in the Agreement purports to sell, transfer or in any way convey Lucidtech’s Intellectual Property Rights. ## **2. LIMITATION OF USE** **T**he User may not distribute, sell, license, rent, lease, sub-license, assign or transfer any rights granted to the User by this Agreement and other rights related to the Service to a third party or authorize others to utilize the Service. You may not reverse engineer, decompile, disassemble, or otherwise seek to obtain the source code, or remove, modify or obscure any proprietary contained within the Service. ## **3. USER CONTENT** All content uploaded to, transferred through, posted, processed or entered into the Service by the User shall remain the sole responsibility of the User. Lucidtech shall have no liability or responsibility for any user content. The User represents and warrants that the content uploaded on or through the Service by the User does not violate any third party's rights, including the privacy rights, publicity rights, copyrights, contract rights, intellectual property rights or any other rights or law, or is considered unacceptable by Lucidtech. Violation of the terms in Section 5 may result in immediate termination of this Agreement and suspension of your account. ## **4. ACCEPTABLE USE OF THE SERVICE** You commit to ensure that the Service is only used for the purposes of the Service and pursuant to the Agreement. You agree that you will not use the Service in any illegal, disrespectful or unacceptable way, that may indirect or direct harm other users, third parties or Lucidtech. ## **5. USER RESTRICTIONS** If you violate the Agreement, if Lucidtech have a strong suspicion of violation of the Agreement, for security reasons or if or if we are required by law or order from the authorities, Lucidtech may put limitations on your account, including suspending your account for as long as Lucidtech see necessary. ## 6. I**NDEMNIFICATION** You will defend, indemnify and hold Lucidtech harmless from and against all claims (from third parties and/or Users), costs, damages, liabilities and expenses (including reasonable attorneys’ fees) arising out of or in connection with your breach of this Agreement. ## **7. PRIVACY** By using the Service, you agree to having your personal data collected and processed pursuant to our Privacy Policy, whereas this Agreement is the legal basis of the processing of the personal data. For information about how Lucidtech process personal data, where Lucidtech is the data controller, including the rights of the data subjects, please review our [Privacy Policy](privacy-policy.md). ## **8. CONFIDENTIALITY** Confidential information of which the Parties become aware of must be kept confidential, and may not be disclosed to any third party without the consent of the other Party. The confidentiality obligation pursuant to this provision shall not prevent the disclosure of information if such disclosure is demanded pursuant to laws or regulations. The confidentiality obligation shall not prevent the information from being used when there is no legitimate interest in keeping it confidential, for example when it is in the public domain or is accessible to the public elsewhere. The Parties shall take all necessary precautions to prevent unauthorized persons from gaining access to, or knowledge of, confidential information. The confidentiality obligation shall apply to the Parties' employees, subcontractors and any third parties who act on behalf of the Parties in connection with the performance of the Agreement. The Parties may only transfer confidential information to such subcontractors and third parties to the extent necessary for the implementation of the Agreement, provided that they are subject to a confidentiality obligation corresponding to this clause. The confidentiality obligation does not preclude the Parties from using the experience and expertise gained in conjunction with the performance of the Agreement. The confidentiality obligation will continue to apply after the expiry of the Agreement. ## **9. UPDATES AND CHANGES OF THE Service** When Lucidtech is planning significant upgrades and changes in the Service, Lucidtech will reasonably notify the User in advance and ensure that any such changes made in the Service do not adversely impact general performance and delivery of the Service. ## **10. WARRANTY AND LIMITATION OF LIABILITY** The Service is delivered “as is”. The liability of Lucidtech is limited to the delivery of the Service. To the extent permitted by Norwegian law, in no event will Lucidtech be liable to the other for any direct or indirect damages, such as business interruption, loss of data or information of any kind, claims or costs whatsoever, any consequential, incidental, special or punitive damages, or any lost profits or lost revenues resulting from and/or relating to the use of the Service, or damages caused by possible errors or misprints in the Service. Lucidtech’s entire liability is limited to the amount actually paid or payable by User to Partner for the use of the Service during the prior twelve (12) months under this agreement or, if no fees are paid or payable during such period, NOK 100 000. ## **11. TERMINATION** In the instance of a material breach of the Agreements by you or the Partner, Lucidtech may terminate the Agreement with immediate effect. Termination under this clause shall not affect any other rights or remedies Lucidtech may have. Upon termination of the Agreement for any cause, Lucidtech shall prepare, transfer or delete all User data, unless Lucidtech is obliged to store the data for a longer period. ## **12. VALIDITY OF THE AGREEMENT** If any part of this Agreement is found to be invalid due to mandatory statutory law or a final legal judgment, it shall only affect those parts found to be invalid. The remaining parts of the Agreement will still be enforceable. ## **13. GOVERNING LAW AND DISPUTES** This Agreement shall be governed by and interpreted in accordance with Norwegian law. Disputes arising in connection with or as a result of this Agreement shall be settled by court proceedings unless the parties agree otherwise. Any court proceedings shall be brought before Oslo District Court. # Privacy Policy Source: https://docs.cradl.ai/legal/privacy-policy How user data is collected, used, shared, and safeguarded. When you use our services, you’re trusting us with your information. We understand this is a big responsibility and work hard to protect your information and put you in control. “Personal data” means any information relating to a natural person. “Processing” means any operation which is performed on personal data, whether by automated means, such as collection, recording, organisation, storage and use. your information. We understand this is a big responsibility and work hard to protect your information and put you in control. ## **1. Data Controller** This Privacy Policy provides information on how Lucidtech AS dba. Cradl AI (**“Lucidtech”**, **"Cradl AI"**, **“we”** or **“us”**) as a controller processes personal data in our business, our obligations and your rights. ## 2. Information about Lucidtech’s processing of personal data #### Data subjects Lucidtech process personal data about people who: are, or work for, our suppliers, customers or business partners, are applying for a job, or get in touch with us in another way (except employees). #### Types of personal data we collect, purpose and lawful basis for processing We process personal data about people who are connected to our suppliers, customers and business partners, such as contact information, telephone number and address(es), including postal address and e-mail. Such personal data is processed to the extent necessary to administer a contractual relationship and/or fulfil an agreement. The lawful basis for this is GDPR article 6 nr. 1 letter b. In some cases, we store such personal data to comply with legal requirements or to assist law enforcement. We may process your information if reasonably necessary to comply with a legal obligation. The lawful basis for this is GDPR article 6 nr. 1 letter c. We also process such personal data for marketing purposes. The legal basis for such processing is Lucidtech's legitimate interest in sending out information and marketing inquiries to stakeholders. If we use e-mail to send out marketing inquiries, this is only sent to people with whom we have an existing customer relationship or to people who have given consent to receive such marketing inquiries. You can withdraw such consent by following the instructions in any e-mail inquiry. We also process personal data in connection with recruitment of persons for employment. We process information such as name, e-mail address, telephone number, address, information from your CV, application and other provided documents, photo, diploma and our or any given references' assessments related to the applicant. The legal basis for such processing is Lucidtech’s legitimate interest in recruiting people to its business. If you do not get a position with us after a recruitment process, we will delete the information about you shortly, unless you have consented to further storage. Personal data that we process for any purpose will not be kept for longer than is necessary for that purpose. ## **3. Disclosure of personal data to others** We do not disclose personal data to others unless there is a legal basis for such disclosure. We may disclose your personal data to comply with legal requirements or to assist law enforcement. We may disclose your personal data if reasonably necessary to comply with a legal process such as a court order, government/law enforcement investigation or other legal requirements. We use data processors to collect, store or otherwise process personal data on our behalf. For example, we have data processors who deliver systems for IT and operations to us. In such cases, we have entered into data processing agreements to safeguard privacy and information security in all stages of the processing. Our processing of personal data takes place mainly within the EU/EEA area. If processing of personal data takes place outside the EU/EEA area, we ensure that the transfer of personal data takes place in accordance with privacy regulations, for example by using the EU's standard contractual clauses. Contact us if you want more information. ## 4. Data subject’s rights You have the right to request access to your personal data, rectification and erasure of your data. You also have the right to ask us to restrict the processing of your personal data in certain circumstances. You may also ask us to transfer the personal data to another organisation (data portability). There may be exceptions to the abovementioned rights. If you wish to invoke your rights, you can contact us by using the contact information at the bottom of the page, and we will respond to your inquiry as soon as possible and normally within 30 days. ## 5. Security Lucidtech has implemented appropriate technical and organizational measures to safeguard the personal data which it processes, against accidental or unlawful destruction or accidental loss, alteration, unauthorized disclosure or access, and other unlawful forms of processing. Lucidtech uses administrative, technical, and physical measures to safeguard data against loss, theft and unauthorized uses, access or modifications. In case of a breach of security leading to the accidental or unlawful destruction, loss, alteration, unauthorized disclosure of, or access to customers personal data, Lucidtech will inform the customer of the breach without undue delay, including a summary description of the potential impact and a recommendation on measures to mitigate the possible adverse effects of the breach. Subcontractors such as IT-service providers processing data on Lucidtech’s behalf are held by legally binding confidentiality and security requirements. ## 6. Changes to the Privacy Policy This Privacy Policy will be modified to reflect changes in applicable laws or regulations, or changes in our practices or procedures. ## 7. Cookie Policy Lucidtech uses cookies (HTML5 local storage) for storing user credentials when logging into Lucidtech's services. The purpose is to provide a seamless user experience by letting the user stay logged into the Service. Amazon Web Services administers the information. By entering and using our website you agree that cookies are placed in your browser as most browsers are set to automatically accept cookies. If you do not want to accept our use of cookies you can withdraw your consent by changing the settings in the browser. For more information about how to manage browser cookies, please follow the instructions provided by your browser. Use of Google Analytics Our website uses Google Analytics, a web analytics service provided by Google, Inc. (“Google”). Google Analytics uses cookies, which are text files placed on your computer, to help the website analyze how users use the site. The information generated by your use of the website is stored in the cookie. Such information includes, for example, the number of times you have used a certain subpage of our website, the time spent on single pages, the pages on which you left our website, data concerning the location of your access (e.g. city or country) or details on the conversion rate of subpages. We have activated anonymizeIP and can therefore not see your computer’s IP address and connect it with the above mentioned data. The data stored in the cookie will be transmitted to and stored by Google on servers in the United States. Google will use this information for the purpose of evaluating your use of the website, compiling reports on website activity for website operators and providing other services relating to website activity and internet usage. Google may also transfer this information to third parties where required to do so by law, or where such third parties process the information on Google’s behalf. Google will not associate your IP address with any other data held by Google. You may refuse the use of cookies by selecting the appropriate settings on your browser or to install a deactivation-add-on ([http://tools.google.com/dlpage/gaoptout?hl=de](http://tools.google.com/dlpage/gaoptout?hl=de)) to your browser. ## 8. Contact information and complaints You have the right to lodge a complaint about our processing of personal data to the Norwegian Data Protection Authority (Datatilsynet). For any questions regarding this privacy policy or how we process personal data, please contact us via: E-mail: [privacy@cradl.ai](mailto:privacy@lucidtech.ai) Address: Bentsebrugata 31E, 0469 Oslo, Norway. Lucidtech is registered in the Norwegian Register of Business Enterprises with organization number 918 345 787. # Service Level Agreement Source: https://docs.cradl.ai/legal/sla The expected performance standards, availability, and responsibilities of the service provider. This Service Level Agreement (SLA) governs the use of the Service under the provisions of the Service Agreement. Unless otherwise provided herein, this SLA is subject to the provisions of the Service Agreement. ## **1. DEFINITIONS** This SLA is a part of the Terms of Service. Terms of Service define words with a special meaning when written with capital letters. Those definitions will also apply to words in this SLA when written in capital letters. In addition, the following terms shall have the meanings set out below in this SLA: * “**Maintenance**” means the scheduled Unavailability of the Services, as announced by Lucidtech prior to the Services becoming Unavailable. * “**Monthly Uptime Percentage**” is calculated by subtracting from 100% the percentage of minutes during the month in which the Lucidtech Services were Unavailable. Monthly Uptime Percentage measurements exclude downtime resulting directly or indirectly from any SLA exclusion as set out in this SLA section 5. * “**Service Credit**” is a credit denominated in NOK, calculated as set forth below, that we may credit back to an eligible account. * “**Unavailable/Unavailability**” is when Lucidtech Services are not running or not reachable due to Lucidtech’s fault. ## **2. SERVICE COMMITMENT** Lucidtech will use commercially reasonable efforts to make Lucidtech Services running with a Monthly Uptime Percentage of at least 99.6% during any monthly billing cycle (the “Service Commitment”). Subject to the SLA Exclusions (see clause 5), if Lucidtech do not meet the Service Commitment, the Licensee will be eligible to receive a Service Credit.\ \ A Monthly Uptime Percentage of 99.6% means no more than 172.8 min/month of Unavailability. Service Credits are calculated as a percentage of the total charges due on the invoice for the monthly billing cycle in which the Unavailability occurred, applied proportionally to the Services that were Unavailable, in accordance with the schedule below: * For Monthly Uptime Percentage less than 99.6% but equal to or greater than 99.0%, Licensee will be eligible for a Service Credit of 10% of the charges attributable to the affected resources. * For a Monthly Uptime Percentage less than 99.0%, Licensee will be eligible for a Service Credit of 30% of the charges attributable to the affected resources. For example, if the Service is Unavailable for 400 minutes, Licensee would be eligible for a Service Credit for 10% of that usage for the month. Lucidtech will apply any Service Credits only against future payments for the licenses otherwise due from Licensee. Service Credits will not entitle Licensee to any refund or other payment from Lucidtech. A Service Credit will be applicable and issued only if the credit amount for the applicable monthly billing cycle is greater than NOK 100. Service Credits may not be transferred or applied to any other account. ## **3. SUPPORT AND INCIDENT MANAGEMENT** Lucidtech shall provide support services to the Licensee during the term of the Service Agreement. The support services shall be provided with the standard of skill and care reasonably expected from a service provider in the industry. Lucidtech may suspend the provision of the support services if any amount due to be paid by the Licensee under this Agreement is overdue, and Lucidtech has given the Licensee at least 30 days written notice. The support covers incident management, after the Licensee has reported or Lucidtech has discovered incidents or problems in the Service. Licensee may request for support via email: [support@lucidtech.ai](mailto:support@lucidtech.ai). If emergency request via telephone: +4748186984. Issues raised through the support services shall be categorized according to Table 1. Lucidtech shall use reasonable endeavors to respond to requests for support, and in any case within the time periods listed in Table 1. | **Category** | **Description** | Max. response time
(Working hours (\*) / Non-working hours) | | - | - | - | | **Critical** | The Service is inoperable. | 3 hours / 12 hours | | **Serious** |

Deviations resulting in reduced availability of

a core function of the Service.

| 6 hours / 24 hours | | **Minor** |

Deviations from agreed deliveries that do not

involve a core function of the Service.

| 24 hours / 48 hours | *Table 1. Working hours are defined to be the hours between 07.00 to 17.00 CET.* Lucidtech will respond to the support request with either 1. An acknowledgement of receipt of the request 2. If possible, an initial diagnosis in relation to any reported error 3. If possible, an anticipated timetable for action in relation to the request Lucidtech shall use all reasonable endeavors to resolve issues raised through the support services. Licensee shall pay the charges for the support services as set out in the Services Agreement. ## **4. SOLE REMEDY** Unless otherwise provided in the Services Agreement, the sole and exclusive remedy for any Unavailability, non-performance, or other failure to provide the Service is the receipt of a Service Credit (if eligible) in accordance with the terms of this SLA. ## **5. SLA EXCLUSIONS** The Service Commitment does not apply to any Unavailability that results from a suspension or remedial action: 1. caused by factors outside of Lucidtech’s reasonable control, including any force majeure event, Internet access, or problems beyond the demarcation point of the Lucidtech network 2. that results from any actions or inactions of Licensee or any third party 3. that results from the equipment, software or other technology of Licensee or any third party (other than third party equipment within Lucidtech’s direct control) 4. that results from failures of the Service not attributable to Unavailability 5. during Maintenance If availability is impacted by factors other than those used in Lucidtech’s Monthly Uptime Percentage calculation, Lucidtech may issue a Service Credit considering such factors at its own discretion. # Terms of Service Source: https://docs.cradl.ai/legal/tos Rules, obligations, and conditions governing use of the service. ## 1. DEFINITIONS “**You**”, “**Your**” and “**Licensee**” refers to the entity that has ordered services from Lucidtech AS dba. Cradl AI (“**Lucidtech**” or "**Cradl AI**") with legal entity number 918 345 787 MVA or an authorized distributor. Lucidtech and the Licensee may also be referred to as a “**Party**” and together as the “**Parties**”. The delivery of services are subject to the terms in this document (“**Terms of Service**”) and the following documents, applicable at any time (altogether the “**Services Agreement**”): * License Agreement (issued separately) * [Description of Services](dos) * [**Service Level Agreement (“SLA”)**](sla) * [**Data Processing Agreement (“DPA”)**](dpa) * [**End User License Agreement (“EULA”)**](eula) “**Confidential Information**” means information that: 1. is by its nature confidential; 2. is designated in writing by one of the Parties as confidential; 3. the Parties knows or reasonably ought to know is confidential; or 4. Information comprised in or relating to any Intellectual Property Rights of one of the Parties “**End User**” means Licensee’s employees, sub-contractors, customers and any other third parties that have access to the Service. “**Intellectual Property Rights**” means all rights in and to any copyright, trademark, trade name, design, patent, know how (trade secrets) and all other rights resulting from intellectual activity in the industrial, scientific, literary or artistic field and any application or right to apply for registration of any of these rights and any right to protect or enforce any of these rights. “**Representative**” means a person who will have the authority to act on behalf of that Party in all matters concerning the Agreement. “**Service**” means the software and services described in [Description of Service](dos.md) and in the Services Agreement. ## 2. THE SERVICES This Terms of Service sets out the Parties rights and obligations regarding delivery of the Service. The Terms of Service is valid for the Services Agreement to which it accompanies. Upon the Parties’ signature on the License Agreement, Licensee has the limited right to integrate the Service into Licensee’s software subject to the terms of the Services Agreement. Upon payment for the Service, Licensee will have a perpetual, non-exclusive, non-assignable, royalty free license to use for internal business operations anything developed by Lucidtech and delivered to Licensee under the Services Agreement. Lucidtech shall, during the term of the Services Agreement, provide the Service as set out in the Services Agreement. Lucidtech reserves the right to make improvements, add, modify or remove functionality, or correct any errors or defects in the Service at its sole discretion, without any obligation or liability resulting from such act or defects. Lucidtech will however not remove functionality which in Lucidtech’s reasonable opinion must be considered as core functionalities for a service such as the Service without reasonable notification to the Customer. Lucidtech will, as part of the Services, provide the Licensee with Lucidtech’s standard customer support services and will provide a level of Service as set out in a separate SLA. Lucidtech may use sub-contractors to provide the Services including all support and maintenance. ## **3. CHANGES TO THE TERMS OF SERVICE** Lucidtech has the right to make less significant changes, revisions and updates in the Terms of Service, without prior notice. Accordingly, Licensee should revise the Terms of Service regularly for updates. Significant changes will be notified in advance. ## **4. OBLIGATIONS OF THE LICENSEE** The Licensee shall provide Lucidtech with sufficient and correct information required for delivering the Services. The Licensee is responsible for compliance with any applicable legal requirements. Licensee may not distribute, sell, license, rent, lease, sub-license, assign or transfer any rights granted to Licensee by this Terms of Service and other rights related to the Service to a third party or authorize other to utilize the Service without a written agreement with Lucidtech. The Licensee shall not use the Service in a way that violates any laws, infringes on anyone's rights, is offensive, or interferes with the Service or any features on the Service, contradicts with the purpose of the Service as set out in the Services Agreement, including the Description of Services, and undertakes to ensure that all users respect this Terms of Service and the End User License Agreement. The Licensee is responsible for the End User’s fulfilment of the obligations under the EULA. Licensee shall notify Lucidtech immediately of any unauthorized use or any other breach of security. Violation of the terms in Section 7 may result in immediate termination of this Terms of Service. Unless Lucidtech believes an immediate suspension is required, Lucidtech will provide reasonable notice before terminating an account. ## **5. CUSTOMER DATA AND PRIVACY** All content uploaded to, transferred through, posted, processed or entered into the Service by the Licensee and/or End Users shall remain the sole property and responsibility of the Licensee. Lucidtech shall have no liability or responsibility for any Licensee or user content. The Licensee represents and warrants that the content uploaded on or through the Service by the Licensee or End Users, does not violate any third party's rights, including the privacy rights, publicity rights, copyrights, contract rights, Intellectual Property Rights or any other rights. The Licensee or Licensee’s customer is data controller for all personal data Lucidtech processes as a part of providing the Services. Lucidtech is a data processor. The data controller’s and Lucidtech’s duties and responsibilities regarding processing of personal data are set out in Lucidtech’s standard Data Processing Agreement. For information about how Lucidtech collects and uses personal data where Lucidtech is the data controller, including the rights of the data subjects, please review Lucidtech's [Privacy Policy](https://lucidtech.ai/privacy.html). The parties' liability for damage suffered by a data subject or other natural persons which is due to a violation of the General Data Protection Act (Regulation 2016/679), the General Data Act with regulations or other regulations that implement the General Data Protection Act, will follow the provisions of article 82 of the General Data Protection Act. The parties are individually liable for administrative fees imposed pursuant to article 83 of the General Data Protection Act. ## **6. CONFIDENTIALITY** Neither Party may use, disclose or make available to any third party the other Party’s Confidential Information, unless such use or disclosure is done in accordance with this Terms of Service. Each Party must hold the other Party’s Confidential Information secure and in confidence, except to the extent that such Confidential Information: 1. is required to be disclosed according to the requirements of any law, judicial or legislative body or government agency; or 2. was approved for release in writing by the other Party, but only to the extent of and subject to such conditions as may be imposed in such written authorization. This clause will survive the termination of the Services Agreement. ## **7. INTELLECTUAL PROPERTY RIGHTS** All Intellectual Property Rights over and in respect of the Service are owned by Lucidtech. The Licensee does not acquire any rights of ownership to the Service, including, but not limited to any of Lucidtech’s APIs, software, workflows, web applications, configurations of the Service, machine learning technology, machine learning models or trained machine learning model weights. Nothing in the Terms of Service purports to sell, transfer or in any way convey to either Party any of the other Parties Intellectual Property Rights. ## **8. INDEMNIFICATION** A Party shall be fully indemnified in respect of any liability for damages imposed on it in relation to a third party and any legal costs incurred, including the Party’s own costs connected to dealing with the case, in connection with a defect in title, provided that the Party seeking compensation 1. notifies the other Party promptly in writing, not later than 30 days after he receives notice of the claim (or sooner if required by applicable law); 2. gives the other Party sole control of the defense and any settlement negotiations; and 3. gives the other Party the information, authority and assistance necessary to defend against or settle the claim. The Licensee must indemnify, defend and hold Lucidtech harmless from any claims (including third party claims), demands, actions, suits, expenses (including attorney’s fees) and damages (including indirect or consequential loss) resulting in any way from: 1. Licensee’s and Licensee’s employee’s use or reliance on the Service, 2. any breach of the terms of this Terms of Service by the Licensee or any Licensee employee. This clause will remain in effect after termination of the Services Agreement. ## **9. NOTIFICATION OF INCIDENTS AND DEFAULT** The Licensee shall notify Lucidtech of any incident or default, in regards to the Services. If the Licensee wants to submit a demand due the incident or default, the Licensee must specify the complaint in writing within reasonable time after the discovery of the incident or default or by the time the incident or default should have been discovered by the Licensee. ## **10. FORCE MAJEURE** Each Party will be excused from their obligations for any period during which, and to the extent that, such Party or any subcontractor is prevented from performing any obligation under the Terms of Service, in whole or in part, as a result of causes beyond its reasonable control, and without its fault or negligence, including without limitation, strikes, lockouts, acts of terrorism or war, epidemics, communication line failures and power failures. The Party who fails to perform due the force majeure, must give notice to the other Party of the impediment and its effect on its ability to perform. If such notice is not received by the other Party within a reasonable time after the Party who fails to perform knew or ought to have known of the impediment, the Party will be held liable for damages resulting from non-performance. The Licensee may terminate the contract after 100 days if Lucidtech cannot perform its obligations under this contract, and will not be entitled to a refund. ## **11. LIMITATION OF LIABILITY** The Service is delivered “as is”. The liability of Lucidtech is limited to the delivery of the Services and Lucidtech does not guarantee compliance with Licensee’s purposes or legal requirements applicable for the Licensee’s use of the Service. To the extent permitted by Norwegian law, the Parties will in no event be liable for indirect damages, such as business interruption, loss of data or information of any kind, any consequential, incidental, or any lost profits or lost revenues resulting from and/or relating to the use of the Service. Overall damages during a period of 12 months are limited to an amount corresponding to total annual consideration set out in the Services Agreement. The said limitations shall not apply in the case of gross negligence or wilful misconduct or apply to breaches related to processing of personal data. ## **12. TERMINATION** If there is a material breach of contract, the other Party may, after giving the Party that is in breach of contract written notice and a reasonable deadline for remedying the situation, terminate the Terms of Service for breach with immediate effect. Lucidtech reserves the right to suspend delivery of the Services if the Licensee fails to timely pay any undisputed amounts under this Terms of Service, but only after Lucidtech notifies Licensee of such failure and such failure continues for 14 days. In the case of debt rescheduling proceedings, composition with creditors, bankruptcy, or any other form of creditor intervention, in respect of the business of Lucidtech, the Licensee shall be entitled to terminate the Terms of Service or breach with immediate effect, unless otherwise stipulated by mandatory law. ## **13. EXIT** Upon termination of the Services Agreement, Lucidtech shall delete all Licensee’s and End User’s data, any documentation developed specifically for the Licensee, and anything else the Licensee holds the rights to which Lucidtech has possession of. The exit activities shall be carried out so that the work is completed by the end of the Services Agreement, unless the Parties agree otherwise in writing. The Licensee shall initiate and otherwise contribute to the transfer so that Lucidtech may perform the work on exit in a timely manner. ## **14. GOVERNING LAW AND DISPUTES** This Terms of Service shall be governed by and interpreted in accordance with Norwegian law. Disputes arising in connection with or as a result of this Terms of Service shall be settled by court proceedings unless the parties agree otherwise. Any court proceedings shall be brought before Oslo District Court. # Quickstart Source: https://docs.cradl.ai/quickstart Getting started with Cradl AI is fast and simple, you can be up and running with your custom document agent in just a few minutes. ### Step 1: Start with a template The fastest way to get started with Cradl AI is by copying a ready-made **Agent template**. After creating your account, pick a template that best matches your use case. Every setting is fully customizable, so you can fine-tune your Agent to handle your specific document type and workflow. If none of the templates resembles your use case, you can always build an Agent from scratch using the **Custom** option. select template Next step in the onboarding, you’ll also define **how you plan to integrate** your Agent into your workflow (e.g., export to a spreadsheet, database, or third-party app). This won’t set up the actual integration yet, but it helps configure your Agent with the right intent. Once your Agent is created, you’ll be taken to the **Runs view** - the central place where all processed documents are listed. If you selected a template, we’ll automatically include a **sample document** flagged for review, so you can immediately test your Agent. You can also **manually upload documents** at any time by clicking **Import** in the top-right corner, a great way to test before setting up automatic imports. To review results, click on a document in the Runs table or use the *Review* button to open the review interface. Here, you can check the AI’s predictions, make any needed corrections, and export your data by clicking **Confirm** when you’re ready. To review results, click on any document in the **Runs table** or use the **Review** button to open the review interface. Here, you can: * **Check the AI’s predictions** for each field * **Make corrections** as needed - your Agent learns from every edit * **Export your data** by clicking **Confirm** when you're ready. **Note:** Data won't be exported automatically until you've set up an export integration. ### **Step 2: Edit your AI model** To control what your AI model extracts, head to the **Workflows** section in the navigation panel. 1. Click on the **Extract with AI** node. 2. In the panel on the right, you can: * **Add new fields** for the AI to extract * **Define validation rules** to ensure data accuracy * **Add formatters** to make sure the output is consistent Customize your model to match the structure and requirements of your specific documents. *** ### **Step 3: Set up integrations** To connect your AI Agent to the rest of your workflow, you’ll need to configure integrations. In the **Workflows** section, you can: * **Add triggers** to automatically process new documents * **Set up export destinations** (e.g., databases, spreadsheets, or external tools) This ensures your extracted data flows seamlessly into the systems where it’s needed.