> ## Documentation Index
> Fetch the complete documentation index at: https://help.autoady.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Keep IDs through advanced workflows

> Approve a saved brief, follow its generation run, and confirm an exact test-plan preview.

Start with the [runnable account read](/developers/overview#quickstart) if you are new to the API. The examples below start from an existing advanced reviewed brief or a prepared test plan. They are not setup instructions for an empty workspace.

## Before the advanced brief loop

Use an authenticated account with eligible paid-plan API access, owner/media-buyer permission for mutation, and enough AutoAdy creative allowance or credits for generation. Choose the intended assigned client workspace; pass its `client_workspace_id` explicitly when the account has more than one assignment. You need an evidence-backed brief version created by `create-creative-brief` or `edit-creative-brief`, with accessible source observations and already-approved offer/claim records. The ordinary [Studio saved draft](/creative/briefs) is a different object and cannot supply this `version_id`.

The public API does not currently discover or bootstrap all required approved records and the prepared approved-context hash. A fresh workspace cannot complete that preparation from these calls alone. The [reference](/developers/reference) lists the creation fields; use real records from an already prepared reviewed workflow, or ask [support](/help/contact) about the missing preparation.

The create/edit response identifies the saved version. This is an **illustrative partial response**, not a live account result:

```json Brief version response theme={null}
{
  "data": {
    "brief": {
      "id": "11111111-1111-4111-8111-111111111111",
      "briefId": "22222222-2222-4222-8222-222222222222",
      "version": 1
    }
  }
}
```

Keep `data.brief.id` as `version_id`. `briefId` identifies the family, and `version` is a number; neither replaces the immutable version UUID. Editing returns a new version ID requiring its own review.

## Make and inspect calls

These connected snippets assume you have retained `savedCreateOrEditResponse` and prepared `preparedPlan` as described above. This server-side JavaScript helper keeps HTTP and operation failures visible. Set `AUTOADY_API_KEY` securely and replace the account/workspace placeholders with authorized IDs. Avoid putting keys in browser code.

```javascript theme={null}
const base = "https://www.autoady.io/api/mcp";
const scope = {
  account_id: "act_YOUR_ACCOUNT_ID",
  client_workspace_id: "YOUR_CLIENT_WORKSPACE_ID"
};

async function call(tool, input) {
  const response = await fetch(`${base}/${tool}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.AUTOADY_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ ...input, ...scope })
  });
  const body = await response.json();
  const data = body.data ?? body;
  if (!response.ok || response.status === 207 || data.ok === false ||
      typeof data.error === "string" ||
      data.envelope?.run?.state === "failed") {
    throw Object.assign(new Error("Inspect this operation result"), {
      status: response.status, data
    });
  }
  return data;
}
```

### Read, approve, and generate

Read the saved version before approving it. Run the approval and generation calls only after the reviewer accepts its actual source, claims, and direction.

```javascript theme={null}
const versionId = savedCreateOrEditResponse.data.brief.id;
const loaded = await call("get-creative-brief", { version_id: versionId });
// Present loaded.brief to the reviewer before the next call.
const approved = await call("approve-creative-brief", {
  version_id: loaded.brief.id
});
if (approved.approval.decision !== "approved") {
  throw new Error("Brief approval is required");
}

// Persist this caller-created key BEFORE sending generation.
const originalKey = "brief-image-2026-10-05-example-01";
const generated = await call("generate-from-brief", {
  version_id: approved.approval.briefVersionId,
  media: "image",
  idempotency_key: originalKey
});
const pack = generated.package;
// Persist pack.runId, pack.idempotencyKey, and pack.artifactIds.
```

Choose one key for one intended generation. Retrying that generation uses the original key. New media or a new brief version is new work, with its own review and key.

### Check or resume the original run

```javascript theme={null}
const status = await call("generation-status", { run_id: pack.runId });
console.log(status.run.state, status.result.run);

// When recovery is appropriate, reuse the original key:
const resumed = await call("resume-generation", {
  idempotency_key: originalKey
});
```

`status.run` is the normalized status; `status.result.run` is the durable record. A queued/running state needs another status read, rather than another generation. An uncertain provider outcome needs the original run's recovery. Keep failed/cancelled states and errors for review; a resume call is not a guarantee of success.

Confirm `pack.briefVersionId` matches the version you reviewed, and the returned scope matches your account/workspace. The saved brief carries `sourceSnapshotIds`, `observationIds`, facts, and approved offer/claim version IDs for checking provenance.

The package returns `artifactIds` and `artifactSpecs`, whose entries include `id`, `kind`, and `payload`. For an image artifact, open its `payload.url` for human inspection. For a storyboard/fallback, read the package’s script, scenes, assets, and artifact payload. Check product details, text, claims, and source lineage before approval.

```javascript theme={null}
const artifact = pack.artifactSpecs.find(item => item.id === reviewedArtifactId);
if (!artifact || !pack.artifactIds.includes(artifact.id)) {
  throw new Error("Artifact is not in this package");
}
// Inspect artifact.payload before the reviewer authorizes this call.
await call("approve-generation-artifact", { artifact_id: artifact.id });
```

`reviewedArtifactId` is the ID of the artifact the human actually inspected. A package ID or run ID is not an artifact ID. Approval records review; these calls do not publish Meta ads.

## Preview and approve a test plan

This separate example uses your prepared plan object, `preparedPlan`. It must supply the [preview-meta-test-plan fields](/developers/reference), including matching account/campaign/ad-set/audience/placement scope, an approved brief reference, at least two distinct approved generated-artifact references, allocation totaling 100, dates/window, compatible success/kill rules, budget, and matching evidence lineage.

These provenance references are caller-prepared inputs; setting an `approved` flag is not a way to create a real approval record. The endpoint validates the submitted plan contract rather than discovering those records for you.

```javascript theme={null}
const planned = await call("preview-meta-test-plan", preparedPlan);
const preview = planned.preview;
if (preview.state !== "approval_required" || preview.issues.length !== 0) {
  throw new Error("Resolve plan issues and preview again");
}
// Display the complete preview and obtain approval before continuing.
const confirmed = await call("approve-meta-test-plan", {
  preview, // Preserve the complete returned object unchanged.
  previewHash: preview.previewHash,
  confirmationToken: preview.confirmationToken
});
if (confirmed.preview.state !== "approved_to_prepare") {
  throw new Error("Plan has not been approved to prepare");
}
```

The returned `previewHash` and `confirmationToken` belong to that exact preview. Copy them from it; do not recalculate, invent, or reuse them after changing the plan. A valid HTTP 200 preview may instead be `draft`, with issues and a null confirmation token. Fix those inputs and preview again.

Both preview and approval retain `launched: false`, `providerWrite: false`, and `spendAuthorized: false`. **Approved to prepare** is not a launch or a spending authorization.

## Recognize success, partial work, and refusal

These are **illustrative response excerpts**; the real response also contains the source wrapper and operation-specific fields.

| Result | Read this before continuing |
| - | - |
| Brief approval | `data.approval.briefVersionId` matches the reviewed version; `decision` is `approved`. |
| Generated package | Keep `data.package.runId`, `idempotencyKey`, and `artifactIds`; inspect actual artifacts and run state. |
| Test-plan draft | `data.preview.state` is `draft`, `issues` explains the gaps, and `confirmationToken` is null. |
| Changed preview refusal | `data.ok` is false and `code` is `PREVIEW_CHANGED`; return to preview/review rather than reusing the old pair. |

A provider write can return HTTP 207 with partial results. The advanced preparation calls above are not those writes. Representative partial write excerpt:

```json theme={null}
{
  "data": {
    "ok": false,
    "operation_id": "33333333-3333-4333-8333-333333333333",
    "status": "partial",
    "partial": {
      "succeeded": [{ "ad_id": "1", "success": true }],
      "failed": [{ "ad_id": "2", "success": false }]
    }
  }
}
```

Item shapes depend on the write. Keep `operation_id` and any `receipt_id`, check provider state for unresolved items, and follow the original request's recovery. Replaying the whole batch with a new request ID can duplicate completed work. See [error and retry rules](/developers/requests#handle-errors).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.