Skip to main content
Send rows from your ERP or another system directly to Azalt, without uploading an Excel file. Select an existing source adapter by ID and submit the sheet names, column names, and values it expects. Azalt uses the same mapping, validation, duplicate handling, calculations, approval workflow, and source-row history as imports on the /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.
  • curl and jq to follow the examples.
Requests use 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:
For Azalt Cloud, use 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.
Existing adapters are not automatically enabled for JSON imports. Removing the contract or setting 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

The response is an array containing each adapter’s 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:
The detail endpoint returns the accepted sheets, columns, types, limits, and an example request, but not the adapter’s script. Copy its example into a working request file:
Edit 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:
The rows response contains 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:
If a destination form has not been opened for a required site and year, execution is blocked pending confirmation. The response keeps the run ID and returns 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:
For an established automated integration, 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 even run.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-Key is mandatory for POST /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’s mode under 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 idempotencyKey and 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.
Run reads and execution are limited to JSON imports created by the same API-key owner in the same organization, even for managers or administrators. Rotating a key for the same user preserves access. Revoking membership or site access can prevent access to an existing run. These endpoints do not expose other users’ runs or older file-upload imports; authorized staff can continue using UI import history.

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:
  1. 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.
  2. Deploy matching updated frontend and backend images for a split installation. Upgrading only the frontend does not add the backend endpoints.
  3. Preserve /api/v1 routing and the Authorization, Content-Type, and Idempotency-Key headers at the ingress/proxy. Do not cache authenticated responses.
  4. Check request-body and synchronous-request timeout limits, then verify the deployed OpenAPI document and run a small reviewed preview before execution.
No S3, MinIO, original-file upload, new storage environment variables, or background queue is required for JSON imports or their saved source-data preview. Existing file imports keep their own storage requirements. If rolling back application images, retain the new request table so retry history survives. Dropping it removes API run lookup and deduplication history, even though destination records and source snapshots remain. Do not replay old batches after removing that history.