/data page.
This guide covers the source adapter REST API, not the MCP server or direct activity creation.
Before you start
Your installation must include the JSON source import API release and its database migration. Check your installation’s/api/v1/openapi.json for /source-adapters and /source-imports; older releases do not expose these endpoints. Publishing this guide does not upgrade an installation. See On-premises and Helm split deployment below.
You need:
- An active source adapter with a JSON API input contract configured by an organization owner.
- An API key from Settings → API keys (
/settings/api-keys) in the intended organization. Use a dedicated integration user and store its key in a secret manager, not source control or shared logs. - A Collector, Approver, Manager, or Owner role to submit and execute imports, with access to the target sites. Viewer access is sufficient for discovery and permitted run reads.
curlandjqto follow the examples.
Authorization: Bearer YOUR_API_KEY. The key determines the organization and user; changing the organization in the browser does not retarget the key. Role, active membership, and site access are checked against current permissions.
Set AZALT_API_KEY securely in your environment. Set the base URL to your own installation, without a trailing slash or /api/v1 suffix:
https://app.azalt.co once the API release is deployed there. On-premises integrations use their own Azalt hostname.
1. Enable an adapter
An organization owner opens Customization → Source adapters, edits the adapter, and fills in JSON API input contract. Keep the adapter active and save the configuration. The following uses fictional invoice values and is a template, not a universal invoice format. The contract must match the sheet and column names consumed by your adapter’s script. Adding a contract validates and describes input; it does not change the script or its destination mappings.enabled to false stops new JSON imports without disabling file uploads. Previously saved previews retain their mapped drafts.
Accepted data types
Fields can have
description, example, required (default false), and nullable (default false). required means the key must be present, not that a string must be non-empty. Explicit null requires nullable: true; omitted optional fields are not filled in. Dates remain strings, and values are not automatically converted between types. Declare a localized amount such as "1.234,50" as string only if your adapter parses that format.
Sheet and column names are exact and case-sensitive. Unknown sheets are rejected. Unknown columns are rejected unless the sheet sets additionalColumns: true. A required sheet must contain at least one row. Names cannot have surrounding whitespace or reserved object keys. Cells cannot contain nested objects or arrays, NUL characters, or invalid Unicode.
Source values, including whitespace, zero, booleans, nulls, and negative numbers, are preserved. If destination amounts must be positive, implement that transformation in the adapter; the API does not automatically take absolute values. The saved source still shows the submitted negative value.
2. Discover adapters and their contracts
id, name, description, isActive, apiReady, and adapterVersion. Choose an adapter with apiReady: true. Increase offset to retrieve another page; limit defaults to 50 and cannot exceed 100.
Replace the ID below with the selected adapter’s ID:
submission.json before submitting: replace example rows with your real data and set the correct context.year and, when required, context.siteId. Example values do not guarantee a valid business mapping. Years must be integers from 1900 through 2200; the site ID must be accessible to the integration user.
The request structure looks like this. ADAPTER_ID and COPY_VERSION_FROM_ADAPTER_RESPONSE are placeholders: use the actual ID and the exact 64-character adapterVersion returned by the endpoint. The generated exampleRequest already includes them.
3. Submit a preview
Assign one stable idempotency key to each logical batch. Keep both the key and request body for retries; do not generate a new key after a timeout.preview is the default mode. It saves source rows and mapped drafts but does not write destination records. Each sheet’s first data row has rowIndex: 2, reserving row 1 for spreadsheet-style headers.
Read the row-level results and check whether execution requires additional confirmation:
rows and total. Each row includes sheetName, rowIndex, rawRow, status, warnings, and error. Pagination defaults to 100 rows and is limited to 200. Add status=failed to retrieve only failures; total then counts only matching rows.
4. Execute the saved preview
After reviewing the mapped results and readiness, execute the same run:execution.blocked: true and execution.readiness.missingFormSites. The readiness endpoint also lists the affected forms, sites, years, and source rows.
Only after reviewing and approving those missing form deployments, repeat the execute request with this body:
mode: "execute" on the initial submission previews and executes in one request. Execution is synchronous, not a background queue job. Missing form deployments still require confirmation unless the initial body explicitly sets allowMissingFormSiteCreation: true.
Interpreting the result
An HTTP 200, or evenrun.status: "completed", does not guarantee that every row imported. Valid drafts can execute while other rows fail. Always inspect execution.blocked, the row counts, and individual warnings/errors.
One source row can produce multiple drafts. Actual write counts are
run.activityWriteCount and run.formValueWriteCount. Imported values still follow the normal approval and reporting configuration; import completion does not bypass approval or guarantee appearance in every records view.
JSON imports appear in the normal import history with an “API submission” name. Configured source-document links in Records can search and open the saved source rows in-app or full screen. There is no original Excel workbook to download for a JSON-only submission.
Retries and adapter changes
Idempotency-Keyis mandatory forPOST /source-imports: 1–200 printable ASCII characters, with no spaces. It is scoped to the organization and API-key owner.- Repeating the same key and body returns the same run with
replayed: true. Object key order does not matter; sheet and row array order does. Reusing a key with different data, mode, version, or confirmation settings returns 409. - To execute a preview, call
/source-imports/{id}/execute. Do not change the initial request’smodeunder the original key. The execute endpoint does not require an idempotency header and is safe to retry for the same run. - After a timeout or transient failure, retry the same request/run. Database locks coordinate concurrent retries across replicas and the UI. Execution can have partially committed writes; retrying the same run finishes remaining ready drafts instead of starting another import.
- Different keys create different imports. Cross-batch business deduplication still depends on the adapter’s
idempotencyKeyand duplicate strategy. Do not resend successful rows under a new key unless that strategy handles them. - A script or input-contract change updates
adapterVersion. New submissions with a stale version return 409. Fetch the updated contract, review the changes, and prepare a new batch. Existing previews execute their saved drafts without rerunning an edited adapter script. - If an adapter fails, inspect the stored row errors, correct the input or adapter, then submit a corrected batch with a new key. Repeating the unchanged failed request does not rerun its mapping.
- Deleted runs are unavailable through these endpoints. Permanently deleting a run retains its idempotency reservation; reusing that key returns 409, not a fresh import.
Endpoint reference
All paths below are relative to/api/v1 on your installation. Its /api/v1/openapi.json provides the deployed request and response schemas.
Limits and errors
Each request supports at most 10 MiB of JSON, 10,000 total rows, 50 sheets, 200 columns per row, and 16,000 characters per string cell. Split larger datasets into separate batches with distinct keys. Your ingress or hosting provider may enforce a lower body size or timeout limit.
Business validation failures can be returned as row results rather than HTTP errors. Always inspect the response body before treating a batch as successful.
On-premises and Helm split deployment
The API works in both the combined Next.js application and the standalone backend used by Helm split installations. Use your normal frontend hostname, or a backend hostname if your operator intentionally exposes it. Before enabling the integration, the deployment operator should:- Back up the intended database and apply the release’s additive migration,
20260908000000_add_source_import_api_requests.ts, using the normal Helm migration job/process. It creates the request-deduplication table. - Deploy matching updated frontend and backend images for a split installation. Upgrading only the frontend does not add the backend endpoints.
- Preserve
/api/v1routing and theAuthorization,Content-Type, andIdempotency-Keyheaders at the ingress/proxy. Do not cache authenticated responses. - Check request-body and synchronous-request timeout limits, then verify the deployed OpenAPI document and run a small reviewed preview before execution.

