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
Response envelope
Section titled “Response envelope”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.
Error envelope
Section titled “Error envelope”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_type | When | What the agent should do |
|---|---|---|
validation | 400 / 422 — bad input | Surface field_errors to the user and fix before retry. |
transient | 408 / 429 / 503 / 504 / other 5xx | Retry after retry_after_seconds (default 30). |
permanent | 401 / 403 / 404 | Stop. Reconnect or verify the id with the user. |
requires_user | 409 — conflict | Surface to the user; needs a human decision. |
Discovery
Section titled “Discovery”list_entity_types
Section titled “list_entity_types”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_import_schema
Section titled “get_import_schema”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:
| Field | Type | Required | Description |
|---|---|---|---|
entity_type | string | Yes | e.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_csv_template
Section titled “get_csv_template”Get a CSV template for an entity type. Returns the raw CSV text — a header row plus one example row.
Inputs:
| Field | Type | Required | Description |
|---|---|---|---|
entity_type | string | Yes | e.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.”
list_businesses
Section titled “list_businesses”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.
Import
Section titled “Import”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.
get_upload_url
Section titled “get_upload_url”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:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
entity_type | string | Yes | — | e.g. "customer", "proposal" |
source | string | Yes | — | External system identifier stored with every row (e.g. "airtable"). Drives idempotency. |
filename | string | Yes | — | Original file name — also the suffix of the R2 object key. |
content_type | string | No | text/csv | MIME type of the file. |
byte_size | int | No | — | File size in bytes — used for early rejection of files over the 25 MiB cap. |
mode | string | No | "incremental" | "incremental" (only supported value): upsert by (source, external_id). |
brand_id | uuid | No | — | Job-level brand applied to rows lacking a brand_external_id. |
skip_enrichment | boolean | No | false | Bypass 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.”
start_csv_import
Section titled “start_csv_import”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:
| Field | Type | Required | Description |
|---|---|---|---|
upload_token | string | Yes | The 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_import_status
Section titled “get_import_status”Get the status of an import job by id.
Inputs:
| Field | Type | Required | Description |
|---|---|---|---|
job_id | uuid | Yes | The 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: pending → validating → importing → staged → normalizing → awaiting_review → completed (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_imports
Section titled “list_imports”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:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
limit | int | No | 25 | 1-100 |
offset | int | No | 0 | 0+ |
status | string | No | — | Filter: 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.”
get_dedup_candidates
Section titled “get_dedup_candidates”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.
resolve_dedup_candidates
Section titled “resolve_dedup_candidates”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:
| Field | Type | Required | Description |
|---|---|---|---|
job_id | uuid | Yes | Import job id. |
resolutions | array | Yes | {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.
get_normalization_summary
Section titled “get_normalization_summary”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.
approve_import
Section titled “approve_import”Submit the operator’s decision on an import that’s awaiting review. Terminal — next_action: null.
Inputs:
| Field | Type | Required | Description |
|---|---|---|---|
job_id | uuid | Yes | Import job id. |
approved | boolean | Yes | true finalizes the job; false discards it. |
partial | boolean | No | When true, terminal status becomes partially_approved. |
approved_record_ids | uuid[] | No | Subset of staged record ids to commit when partial=true. |
changes | object | No | Optional review changes (dedup_resolutions, address_reverts); validated server-side. |
retry_normalization
Section titled “retry_normalization”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.
get_import_settings
Section titled “get_import_settings”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.
suggest_column_mapping
Section titled “suggest_column_mapping”Given source column headers (e.g. from an Airtable or spreadsheet export), suggest the best Adva field mapping using AI.
Inputs:
| Field | Type | Required | Description |
|---|---|---|---|
entity_type | string | Yes | The target Adva entity |
source_columns | string[] | Yes | Your source column names. Min 1, max 200. |
sample_rows | object[] | No | Up 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.
get_external_ids
Section titled “get_external_ids”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:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
entity_type | string | Yes | — | e.g. "customer" |
source | string | Yes | — | e.g. "airtable", "salesforce" |
limit | int | No | 100 | 1-500 |
offset | int | No | 0 | 0+ |
Output:
{ "entity_type": "customer", "source": "airtable", "limit": 100, "offset": 0, "total": 1284, "rows": [ { "external_id": "recABC123", "adva_id": "c1000000-..." }, ... ]}