Skip to content

Tool Reference

All tools return JSON serialized as text content (except get_csv_template, which returns raw CSV). Limits and defaults are enforced server-side — invalid inputs fail fast with descriptive errors.

Tools are grouped into three categories:

  • Discovery — what can I import, what does the schema look like, which businesses can I write to
  • Import — validate, submit, track, review, and approve imports through the staged pipeline
  • AI — LLM-assisted column mapping and external-id lookup

Tools that drive the staged-import lifecycle (the import tools below) wrap their result in a {data, next_action} envelope so agents follow a deterministic state machine instead of guessing what to call next:

{
"data": { "import_job_id": "550e8400-..." },
"next_action": {
"action": "poll_status",
"params": { "job_id": "550e8400-..." },
"delay_seconds": 5
}
}

next_action is null for terminal states (completed, discarded, partially_approved) and for purely informational tools (list_imports, get_import_settings).

Discovery and AI tools return the body directly without an envelope — they don’t drive a lifecycle.

Failed tool calls set isError: true and return a structured envelope so agents can route programmatically:

{
"error_type": "validation",
"message": "validation failed",
"suggested_action": "Fix the input fields and retry. Do not retry the request as-is.",
"field_errors": [{ "field": "email", "row": 12, "message": "invalid format" }]
}
error_typeWhenWhat the agent should do
validation400 / 422 — bad inputSurface field_errors to the user and fix before retry.
transient408 / 429 / 503 / 504 / other 5xxRetry after retry_after_seconds (default 30).
permanent401 / 403 / 404Stop. Reconnect or verify the id with the user.
requires_user409 — conflictSurface to the user; needs a human decision.

List the entity types the user can import into Adva, ordered by recommended import tier.

Inputs: none.

Output:

{
"entities": [
{ "entity_type": "product", "tier": 1, "description": "Catalog items you sell or install" },
{ "entity_type": "territory", "tier": 1, "description": "Geographic sales regions" },
...
]
}

Example prompt:

“What kinds of records can I import into Adva?”


Get a JSON Schema for a given entity type. Use this to map your source file’s columns to Adva fields before uploading it via get_upload_url + start_csv_import.

Inputs:

FieldTypeRequiredDescription
entity_typestringYese.g. "customer", "proposal", "order", "product"

Output: a Draft 2020-12 JSON Schema describing the fields, types, required columns, and enums for the entity.

Example prompt:

“Show me the schema for a customer record in Adva.”


Get a CSV template for an entity type. Returns the raw CSV text — a header row plus one example row.

Inputs:

FieldTypeRequiredDescription
entity_typestringYese.g. "customer", "proposal", "product"

Output: plain CSV text, ready to paste into a spreadsheet.

Example prompt:

“Give me a CSV template for proposals. I’ll fill it in and upload.”


Read-only orientation: lists the businesses your credential can see.

Inputs: none.

Output:

{
"businesses": [
{
"id": "b0000000-...",
"name": "Southern Turf Care",
"slug": "southern-turf"
}
]
}

Note: your credential is already bound to one business — the import tools (get_upload_url, start_csv_import) always write there. This tool does not switch the binding. To target a different business, follow the steps in Key Scope: One Business. API keys are bound server-side, so in API-key mode this will only ever return the one business the key belongs to.


Imports are file-only (ADV-1068). The agent never authors or regenerates import payloads in-context — it uploads the source file’s bytes directly and the server validates and imports them. This two-step flow (get_upload_url then start_csv_import) is the only supported way to submit import data.

Step 1 of the import flow. Mints a short-lived pre-signed R2 PUT URL plus an opaque upload_token. The agent uploads the source file’s bytes directly to the returned URL — they never travel through the LLM context, so values such as external_id cannot be fabricated or omitted in transit.

Inputs:

FieldTypeRequiredDefaultDescription
entity_typestringYese.g. "customer", "proposal"
sourcestringYesExternal system identifier stored with every row (e.g. "airtable"). Drives idempotency.
filenamestringYesOriginal file name — also the suffix of the R2 object key.
content_typestringNotext/csvMIME type of the file.
byte_sizeintNoFile size in bytes — used for early rejection of files over the 25 MiB cap.
modestringNo"incremental""incremental" (only supported value): upsert by (source, external_id).
brand_iduuidNoJob-level brand applied to rows lacking a brand_external_id.
skip_enrichmentbooleanNofalseBypass the normalization pipeline — only for pre-enriched data.

Output: { upload_url, headers, expires_at, upload_token }. PUT the file bytes to upload_url with the returned headers, then pass the entire upload_token to start_csv_import.

Example prompt:

“Import customers.csv from my Airtable export. Source: airtable.”


Step 2 of the import flow. After the file is uploaded, pass the upload_token here. The server reads the object from R2, validates it, and runs the same SHA-256 dedup / validation / Workflow pipeline as the REST /upload endpoint. Returns an import job id — poll get_import_status until the job reaches a terminal state.

Inputs:

FieldTypeRequiredDescription
upload_tokenstringYesThe opaque token returned by get_upload_url. Pass it back verbatim.

Output:

{
"import_job_id": "550e8400-e29b-41d4-a716-446655440000",
"uploaded_file_id": "660e8400-...",
"entity_type": "customer",
"poll_url": "/api/v1/import/jobs/550e8400-..."
}

Validation runs server-side on the uploaded file. Every row is checked — external_id is required and must be a real, non-blank reference to a source record. If any row is missing or has a blank external_id, the whole import is rejected before any record is written and the job transitions to failed with row-level errors visible via get_import_status. A file with missing external_ids is never partial-imported.

Idempotency: each record’s external_id plus the top-level source forms a unique key. Re-running the same file with the same external_ids updates existing records instead of duplicating.

Example prompt:

“The upload finished — start the import.”


Get the status of an import job by id.

Inputs:

FieldTypeRequiredDescription
job_iduuidYesThe import_job_id returned by start_csv_import

Output:

{
"id": "550e8400-...",
"entity_type": "customer",
"status": "completed",
"total_rows": 98,
"processed_rows": 98,
"valid_rows": 96,
"error_rows": 2,
"created_count": 94,
"updated_count": 2,
"skipped_count": 0,
"errors": [ ... ],
"warnings": [],
"started_at": "2026-04-16T15:30:00Z",
"completed_at": "2026-04-16T15:30:12Z"
}

Status values: pendingvalidatingimportingstagednormalizingawaiting_reviewcompleted (or partially_approved/discarded/failed/expired). The next_action returned with each status tells the agent whether to keep polling, fetch the normalization summary, retry, or stop — see the Response envelope above.


List recent import jobs for the current business, most recent first. Also returns pending_review_count so the agent can detect outstanding review work in a single call.

Inputs:

FieldTypeRequiredDefaultDescription
limitintNo251-100
offsetintNo00+
statusstringNoFilter: pending, validating, importing, staged, normalizing, awaiting_review, partially_approved, completed, failed, expired, discarded

Output (inside the envelope’s data):

{
"imports": [{ "id": "550e8400-...", "status": "awaiting_review" }],
"total_count": 12,
"pending_review_count": 2
}

next_action is always null — informational.

Example prompt:

“Show me my most recent failed imports from this week.”


List dedup candidate pairs for an import job, ordered by confidence DESC. Each pair surfaces the imported and existing contact with a confidence score so the user can decide whether to merge.

Inputs: job_id (uuid, required), plus optional status, limit, offset.

Next action: resolve_dedup_candidates if any pairs are still pending, otherwise approve_import.


Record resolutions for one or more dedup candidate pairs. Idempotent for the same candidate_id + action; same id with a different action returns requires_user.

Inputs:

FieldTypeRequiredDescription
job_iduuidYesImport job id.
resolutionsarrayYes{candidate_id, action} where action ∈ {merge_keep_a, merge_keep_b, keep_both, dismiss}.

Next action: approve_import when remaining_count === 0, otherwise loop back to get_dedup_candidates.


Returns a flat digest of normalization results — counts from normalization_result, the current dedup pairs, and how many can be auto-resolved. Use right before approve_import so the user sees what changed.

Inputs: job_id (uuid).

Next action: get_dedup_candidates if there are unresolved manual-review pairs, otherwise approve_import.


Submit the operator’s decision on an import that’s awaiting review. Terminal — next_action: null.

Inputs:

FieldTypeRequiredDescription
job_iduuidYesImport job id.
approvedbooleanYestrue finalizes the job; false discards it.
partialbooleanNoWhen true, terminal status becomes partially_approved.
approved_record_idsuuid[]NoSubset of staged record ids to commit when partial=true.
changesobjectNoOptional review changes (dedup_resolutions, address_reverts); validated server-side.

Retry normalization for a failed or expired import. Dispatches a fresh workflow run and resets job state to staged.

Inputs: job_id (uuid).

Next action: poll_status with delay_seconds: 5.


Read a business’s import settings: auto-approve toggles for imports / dedup / address corrections, notification channel and frequency, review timeout. Read-only via this tool — updates go through the dashboard or PATCH /core/businesses/{id}/settings.

Inputs: business_id (uuid). Cross-tenant requests return a permanent 404 error.

next_action is always null.


Given source column headers (e.g. from an Airtable or spreadsheet export), suggest the best Adva field mapping using AI.

Inputs:

FieldTypeRequiredDescription
entity_typestringYesThe target Adva entity
source_columnsstring[]YesYour source column names. Min 1, max 200.
sample_rowsobject[]NoUp to 5 sample rows from your source to help the AI infer semantic types.

Output:

{
"mappings": [
{
"source": "Full Name",
"target": null,
"confidence": 0.0,
"rationale": "Adva splits names into first_name + last_name; user must split on client side"
},
{
"source": "Phone #",
"target": "phone",
"confidence": 0.95,
"rationale": "Exact semantic match for phone number"
}
]
}

target is null when the model can’t find a confident match — treat those as “needs human review” and don’t auto-apply them.

Always review mappings before uploading the file via get_upload_url + start_csv_import. The model (Llama 3.1 8B) is good at obvious matches but can hallucinate on unusual column names.


List external ids already linked to Adva entities for a given source. Use this to answer “has this record already been imported?” before deciding between insert and update.

Inputs:

FieldTypeRequiredDefaultDescription
entity_typestringYese.g. "customer"
sourcestringYese.g. "airtable", "salesforce"
limitintNo1001-500
offsetintNo00+

Output:

{
"entity_type": "customer",
"source": "airtable",
"limit": 100,
"offset": 0,
"total": 1284,
"rows": [
{ "external_id": "recABC123", "adva_id": "c1000000-..." },
...
]
}