Getting started

This page is the Snapshot API procedure: what to set up on the target Agent by hand, then each call. Every request and response comes from a real run, so you can compare against it.

For what a snapshot carries, the pre-import checks, and the limits, see the Snapshot API overview and What a snapshot includes.

Quick start

The shortest safe path, in seven steps. The sections below give the full checklist, each request with a real response, and what to do when a job fails.

1

Get the target Agent ready

Work through the Prerequisites: connections, features, tags, secret values, and an export of the target if it has anything on it.

2

Export the source Agent

GET /v2/config/ with the source key. Poll the job until completed, then download result.url.

3

Choose what to import

Decide which sections you need, for example ["playbooks"]. Leave include out only when you mean to replace everything.

4

Validate on the target

PUT /v2/config/ with {"mode": "validate", "include": [...]} and the target key. Upload the archive to upload.url. Poll until completed, then read result.blockers, result.changes, and result.selection.

5

Fix every blocking issue

Each one names its fix. Repeat the validate until result.status is valid, and read the delete counts.

6

Apply

PUT /v2/config/ with {"mode": "apply", "include": [...]} and the target key. Upload the same archive to the new upload.url. Poll until completed. apply is the only mode that writes, and you have to ask for it by name.

7

Finish on the target

Work through After the apply: read warnings, enter what the snapshot could not carry, then test before going live.

Prerequisites

Nothing in this list can be done through the Snapshot API. Work through it in the dashboard before the first validate. Your Ada team does the items marked Ada on request.

Both Agents

  • An API key for each Agent. Go to Config > PLATFORM > API keys and click New API key. Name it, then copy the key from the dialog. It is shown once.

    The New API key dialog showing the generated key and the Copy button

Target Agent, before the first validate

  1. Export the target first if it has anything on it. That archive is your only way back.

  2. Live-agent platforms connected. Every platform the source’s Handoffs route to is connected and enabled under Config > AI AGENT > Handoffs > Integrations. A card that is disabled, or that still shows Connect, blocks the import.

    The Handoffs page, Integrations tab, with a Zendesk Support card that is disabled
  3. Same accounts as the source. Under Config > PLATFORM > Apps, Zendesk and Salesforce must point at the same subdomain or org the source uses. A different account blocks the import. The checks do not detect the same account with wrong credentials; check it yourself.

    The Apps page with a Zendesk card and its Connect button
  4. Integrations enabled and connected. Every integration the source’s API tools or Handoffs call is both enabled and connected on Apps. Enabled without credentials blocks the import.

  5. Customer login set up, if any API tool on the source signs end users in. It does not travel, and the checks do not report it. Set it up under Config > AI AGENT > Tools > Manage tokens. After the import, edit each API tool that used it to select the new token.

  6. Knowledge sources reconnected, pointing at the same knowledge base as the source, so imported articles keep syncing. Web import sources need nothing; their crawl settings travel.

  7. Knowledge tags created. Every tag the source’s articles use must exist on the target with the same id. Create them through the Knowledge API. An article whose tag is missing fails to import. A same-Agent restore is unaffected.

  8. Features match the source (Ada). Ask your Ada team to enable on the target whatever is enabled on the source. A missing feature blocks the import when the checks can see it. When they cannot, the import succeeds and the Agent misbehaves later.

  9. Secret values at hand. Read result.secrets on the source export job for the names. These are the static tokens your API tools use, under Config > AI AGENT > Tools > Manage tokens. Have each value ready for the apply request.

  10. Room for the snapshot. The target’s limits on enabled API tools, live Custom instructions, Glossary terms, and articles must fit what the snapshot carries.

Authenticate

Every call except the archive upload carries the API key of the Agent it targets. The examples below use EXAMPLE.ada.support and an ADA_API_KEY environment variable; substitute your Agent’s host and key.

$export ADA_BASE="https://EXAMPLE.ada.support/api"
$export ADA_API_KEY="your_api_key"

Export a snapshot

An export starts a background job and returns its id. The archive becomes available on the job once it completes. An export always captures the whole Agent; you choose what to import.

To export an AI Agent:

  1. Start the export.

    $curl -sS -X GET "$ADA_BASE/v2/config/" \
    > -H "Authorization: Bearer $ADA_API_KEY"
    1{"job_id": "config_job_ccc3574a9b9342dbac32c50c0a656b93", "status": "queued"}

    If an export is already running for this Agent, the response carries that job’s id and its current status, which may already be in_progress. Poll the job_id either way.

  2. Poll the job until status is completed.

    $curl -sS "$ADA_BASE/v2/config/jobs/config_job_ccc3574a9b9342dbac32c50c0a656b93" \
    > -H "Authorization: Bearer $ADA_API_KEY"
    1{
    2 "job_id": "config_job_ccc3574a9b9342dbac32c50c0a656b93",
    3 "type": "export",
    4 "status": "completed",
    5 "progress": 100,
    6 "result": {
    7 "url": "https://…/config_20260904-145213.tar.gz?…",
    8 "expires_at": "2026-09-04T15:52:13Z",
    9 "secrets": {}
    10 },
    11 "warnings": null,
    12 "failure_reason": null,
    13 "failure_detail": null
    14}
  3. Download the archive with curl, or with a browser that keeps the file as .tar.gz. Safari unpacks the file and keeps the name, and the import then rejects it.

    $curl -sS -f -o snapshot.tar.gz "PASTE result.url HERE"

    -f makes curl fail on an expired link instead of saving the error page as your snapshot. If the link has expired, poll the job again for a fresh one.

  4. Note the names under result.secrets. These are the secret values the target must have; you supply them in the apply request.

Keep the archive somewhere safe. The job record, and with it the download link, is gone after 7 days.

Choose what to import

The import request takes an optional include list. Name the sections you want, and the import brings along whatever those sections reference. Leave include out to import the whole snapshot.

The section names, and what each import replaces or only adds to, are under Choosing sections.

A name that is not a section is refused before any upload:

1{
2 "errors": [{
3 "type": "validation_error",
4 "message": "Request body validation error",
5 "details": [{
6 "parameter": "$.include[0]",
7 "location": "body",
8 "message": "'not_a_section' is not one of ['persona', 'variables', 'actions', …]"
9 }]
10 }]
11}

The examples below import Playbooks only. To import everything, leave out include and read every delete count before you apply.

Validate a snapshot

A validate reads the target and reports what an apply would do, without writing anything. The one exception is secrets: values you send are stored on the Agent at once, so leave it empty until the apply. A validate is also what an import does when you send no mode.

To validate a snapshot against the target Agent:

  1. Request the import in validate mode, using the target Agent’s host and key.

    $curl -sS -X PUT "$ADA_BASE/v2/config/" \
    > -H "Authorization: Bearer $ADA_API_KEY" \
    > -H "Content-Type: application/json" \
    > -d '{"mode": "validate", "include": ["playbooks"]}'
    1{
    2 "job_id": "config_job_97fade03e7ac4a5f88eaa23227e68aa3",
    3 "status": "waiting_for_upload",
    4 "upload": {
    5 "url": "https://…/upload.tar.gz?…",
    6 "method": "PUT",
    7 "expires_at": "2026-09-04T15:52:59Z"
    8 }
    9}
  2. Upload the archive to upload.url. No Authorization header, no content type, the raw file in one request.

    $curl -sS -f -X PUT -T snapshot.tar.gz "PASTE upload.url HERE"

    A 200 with an empty body means the upload succeeded. Check the status code yourself; the job cannot see this request. If no archive arrives, the job fails after about 90 minutes with upload_not_received. If the upload failed, request the import again. A job still waiting for its archive is replaced, not refused, and ends as failed with superseded.

  3. Poll the job until status is completed. Allow up to two minutes in waiting_for_upload after the upload arrives.

    1{
    2 "status": "completed",
    3 "result": {
    4 "status": "valid",
    5 "source": "s3://…/upload.tar.gz",
    6 "source_bot_handle": "EXAMPLE",
    7 "articles": {"found": 0},
    8 "selection": {
    9 "requested": ["playbooks"],
    10 "included": ["actions", "playbooks", "variables"],
    11 "pulled": {"actions": 1, "variables": 1},
    12 "unresolved": []
    13 },
    14 "blockers": [],
    15 "warnings": [],
    16 "changes": {
    17 "variables": {"create": 0, "update": 1, "delete": 0, "created": [], "updated": ["order_id"], "deleted": []},
    18 "actions": {"create": 0, "update": 1, "delete": 0, "created": [], "updated": ["Get order status"], "deleted": []},
    19 "playbooks": {"create": 0, "update": 1, "delete": 0, "created": [], "updated": ["Order status"], "deleted": []}
    20 }
    21 },
    22 "warnings": [],
    23 "failure_reason": null,
    24 "failure_detail": null
    25}

    This example is a same-Agent restore of one Playbook. selection shows that the Playbook pulled in one API tool and one variable, and changes names them. Nothing is created or deleted.

  4. Read the result. status: blocked with entries in blockers means the apply would be refused for the same reasons. Fix each one on the target (see Pre-import checks) and validate again. Read warnings, changes, and selection.unresolved before you apply. Every non-zero delete count is a real deletion the apply will perform.

To validate the whole snapshot, send the same request without include. The result then has selection: null and a changes entry for every section, including the articles:

1"changes": {
2 "variables": {"create": 0, "update": 4, "delete": 0, "created": [], "updated": ["email", "address", "full_name", "order_id"], "deleted": []},
3 "actions": {"create": 0, "update": 4, "delete": 0, "created": [], "updated": ["Get account balance", "Get recent transactions", "Request new card", "Get order status"], "deleted": []},
4 "handoffs": {"create": 0, "update": 1, "delete": 0, "created": [], "updated": ["Handoff"], "deleted": []},
5 "custom_instructions": {"create": 0, "update": 3, "delete": 0, "created": [], "updated": ["Confirm if you've helped", "Explaining financial terms", "Order status guidance"], "deleted": []},
6 "knowledge.sources": {"create": 0, "update": 1, "delete": 0, "created": [], "updated": ["Created in Ada"], "deleted": []},
7 "knowledge.articles": {"create": 0, "update": 5, "delete": 0, "created": [], "updated": ["Understanding Our Fees", "Setting Up Account Alerts", ""], "deleted": []}
8}

knowledge.articles.delete is the number to check before applying to an Agent with live Knowledge.

Apply a snapshot

An apply makes the changes. Run it only after a validate with the same archive and the same include returned valid, and you have read its warnings and change counts.

To apply a snapshot to the target Agent:

  1. Request the import in apply mode, with the same include. Add the secret values the source export listed.

    $curl -sS -X PUT "$ADA_BASE/v2/config/" \
    > -H "Authorization: Bearer $ADA_API_KEY" \
    > -H "Content-Type: application/json" \
    > -d '{"mode": "apply", "include": ["playbooks"], "secrets": {"crm_api_token": "the-value"}}'

    The response has the same shape as the validate request, with a new job_id and a new upload.url.

  2. Upload the same archive again, to the new link. The link from the validate round cannot be reused.

    $curl -sS -f -X PUT -T snapshot.tar.gz "PASTE the new upload.url HERE"
  3. Poll the job until status is completed or failed.

    1{
    2 "status": "completed",
    3 "result": {
    4 "source": "s3://…/upload.tar.gz",
    5 "source_bot_handle": "EXAMPLE",
    6 "articles": {"upserted": 0, "deleted": 0},
    7 "indexing": "backfill_enqueued",
    8 "selection": {
    9 "requested": ["playbooks"],
    10 "included": ["actions", "playbooks", "variables"],
    11 "pulled": {"actions": 1, "variables": 1},
    12 "unresolved": []
    13 },
    14 "post_apply_blockers": []
    15 },
    16 "warnings": [],
    17 "failure_reason": null,
    18 "failure_detail": null
    19}

    articles is zero because knowledge was neither requested nor pulled in. post_apply_blockers empty is the healthy answer.

  4. Finish on the target: work through After the apply.

After the apply

  • Read warnings on the job. Each names a section and what to do.
  • Enter what the snapshot could not carry: secret values not passed in the request, Authorization headers on API tools, and tags. Reconnect MCP servers and re-select them in Playbook steps. Re-select any off-hours response a warning flagged.
  • Add conversation start events to imported Playbooks. They do not travel. The “When to use this Playbook” text does.
  • Confirm connected Knowledge sources are syncing, and allow time for search to catch up.
  • Run test conversations before anything goes live.

When a job fails

One endpoint reports every job. failure_reason is a short code; failure_detail is a sentence you can show someone. The most common failures:

You seeMeaningDo
failed, failure_reason: "preflight_failed"The pre-import checks refused the apply. result.blockers lists each issue.Fix them on the target, then request a new import. The same request without fixes fails again. Check Apps: integrations the snapshot lists were enabled before the checks.
failed, failure_reason: "invalid_snapshot" or "source_unreadable"The archive is malformed, truncated, or not a snapshot. Nothing was written.Read failure_detail, re-export, then import the new archive.
failed, failure_reason: "superseded"You requested the import again while this job was waiting for its archive.Poll the newer job. Expected, not an error.
failed, failure_reason: "timeout"The job hit its time limit before finishing.Request the same import again and upload the same archive. It resumes where it stopped.
failed, failure_reason: "incomplete_knowledge"The configuration sections were applied, including their deletions. The Knowledge step did not finish, so no articles were deleted.Request the same import again and upload the same archive. It resumes where it stopped.
failed, failure_reason: "partially_applied"The apply stopped after some configuration was written.Do not simply retry. Check the Agent in the dashboard, then import an archive you know is good.
failed, failure_reason: "archive_expired"The export’s archive has passed its retention.Export again.
progress: 100 but status is not completedFinal steps still running.Keep polling. Only status means done.

The full list of codes is under Failure reasons. HTTP errors on the request itself, such as 409, are listed under Error responses.

Move a snapshot between regions

An archive exported from an Agent in one region imports into an Agent in another. Download it from the first Agent’s host and upload it to the second Agent’s upload link. The two regions need no access to each other. Everything in Prerequisites applies unchanged.

Choose a tool

The calls above are plain HTTP and work from any client. Two guided paths: