iAdvizeDocs

Errors and rate limits

The problem document every API error returns, the list of error codes, what causes each one, and how to handle rate limits.

Every error from /api/v1/ is an RFC 9457 problem document, served with the content type application/problem+json. The old { "error": "message" } body no longer exists. A client that parsed it needs to read code instead.

{
  "type": "https://iadvize.ai/docs/api/errors#not_found",
  "title": "Resource not found",
  "status": 404,
  "detail": "Site not found",
  "instance": "/api/v1/sites/site_01example",
  "code": "not_found",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}
FieldTypeMeaning
typestringURL of the section below that describes this code.
titlestringShort summary of the code. The same for every error with that code.
statusnumberThe HTTP status, repeated from the response.
detailstringWhat went wrong on this request. Written for people; its wording can change at any time.
instancestringThe path of the request that failed.
codestringStable identifier of the error. Branch on this, never on detail.
requestIdstringQuote it when you contact support. It is the platform's request id when one is set, otherwise an opaque id starting req_.
errorsarrayOnly on some responses: one entry per field at fault on a 400 (see Field errors), one per unmet condition on a version_not_ready.
savedAt, savedByTypestring or nullOnly on a version_changed: when the latest save on the version was made, and through which channel.

Codes are never renamed or reused. New codes can appear without a new API version, so treat an unknown code as a generic failure of its HTTP status.

Accepted work

An endpoint that starts work which goes on after the response answers 202 Accepted, never 200. The body says what was accepted and what was skipped and why. There is no job to poll: read the progress from the resource the work acts on, for example the status of a page. Adding pages, reading pages, asking for starter questions and starting a sitemap import work this way. A sitemap import is the one exception to "no job to poll": it has its own resource, which you read at GET /api/v1/sites/{siteId}/pages/imports/{importId}.

Field errors

A validation error lists each offending field in errors. Each entry has a code and a detail, plus the location of the field:

  • pointer: an RFC 6901 JSON Pointer into the request body, for example /name. An empty pointer means the body as a whole.
  • parameter: the name of a query parameter, for example cursor.

An entry carries one of the two, depending on where the field is.

unauthorized

Status 401. The API key is missing, malformed, unknown, revoked or expired. The response is the same for all of these on purpose, so it never tells you which one it is.

What to do: check that the Authorization: Bearer <key> header is present and holds the full key. If the key was revoked or has expired, create a new one. Retrying the same request with the same key will not help.

{
  "type": "https://iadvize.ai/docs/api/errors#unauthorized",
  "title": "Missing or invalid API key",
  "status": 401,
  "detail": "Missing or invalid API key",
  "instance": "/api/v1/sites",
  "code": "unauthorized",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

invalid_request

Status 400. The request body or a query parameter failed validation. errors lists every field at fault.

What to do: fix the fields named in errors and send the request again. Do not retry it unchanged.

{
  "type": "https://iadvize.ai/docs/api/errors#invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "Too small: expected string to have >=1 characters",
  "instance": "/api/v1/sites",
  "code": "invalid_request",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11",
  "errors": [
    {
      "code": "too_small",
      "detail": "Too small: expected string to have >=1 characters",
      "pointer": "/name"
    }
  ]
}

invalid_cursor

Status 400. A pagination cursor was not one the API issued, or was altered. The entry in errors names the cursor parameter.

What to do: pass back the cursor exactly as the previous response returned it, or start again from the first page.

A cursor saved before a change of the cursor format gets this code too, once: start again without a cursor, and the next pages carry cursors in the new format.

The list endpoints take a cursor, so they are the ones that can return this code: conversations, pages, starter questions, starter question performance and engagement exposures. See Filter and paginate conversations.

{
  "type": "https://iadvize.ai/docs/api/errors#invalid_cursor",
  "title": "Invalid cursor",
  "status": 400,
  "detail": "Invalid cursor",
  "instance": "/api/v1/sites/site_01example/conversations",
  "code": "invalid_cursor",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11",
  "errors": [
    {
      "code": "invalid_cursor",
      "detail": "Invalid cursor",
      "parameter": "cursor"
    }
  ]
}

cursor_mismatch

Status 400. The cursor is one the API issued, but for a different set of filters than the ones on this request. A cursor marks a position in one specific listing. The entry in errors names the cursor parameter.

What to do: to read the next page, send the same filters as the request that returned the cursor. To change a filter, start again without a cursor. Changing limit or count between pages is fine, since neither is part of the listing.

{
  "type": "https://iadvize.ai/docs/api/errors#cursor_mismatch",
  "title": "Cursor does not match the filters",
  "status": 400,
  "detail": "This cursor was issued for different filters. Start again without a cursor to change the filters.",
  "instance": "/api/v1/sites/site_01example/conversations",
  "code": "cursor_mismatch",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11",
  "errors": [
    {
      "code": "cursor_mismatch",
      "detail": "This cursor was issued for different filters. Start again without a cursor to change the filters.",
      "parameter": "cursor"
    }
  ]
}

invalid_filter

Status 400. The filter is well-formed but the API refuses it as a whole. The entry in errors names the parameter at fault. Three cases:

  • A label token that the taxonomy does not define, in labels.all, labels.any or labels.none. Read the valid tokens from GET /api/v1/labels/vocabulary.
  • A labels.none filter with nothing to narrow the listing first: it needs labels.all, labels.any, from or to alongside it. labels.none also accepts only tokens that end in :true.
  • A labelStatus other than labeled combined with a labels.* or score filter, since only labeled conversations have labels. The entry names labelStatus.

What to do: fix the parameter named in errors and send the request again. Do not retry it unchanged.

{
  "type": "https://iadvize.ai/docs/api/errors#invalid_filter",
  "title": "Invalid filter",
  "status": 400,
  "detail": "unknown label or value for taxonomy v1.2",
  "instance": "/api/v1/sites/site_01example/conversations",
  "code": "invalid_filter",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11",
  "errors": [
    {
      "code": "invalid_filter",
      "detail": "unknown label or value for taxonomy v1.2",
      "parameter": "labels.all"
    }
  ]
}

A parameter the endpoint does not define, or a single-valued parameter sent twice, is an invalid_request, not an invalid_filter. Its errors entry has the code unknown_parameter or duplicate_parameter.

language_not_enabled

Status 400. A write names a language the site does not serve. Nothing was written. This is what a starter question returns when its texts hold a language that is not enabled on the site.

What to do: enable the language in the site's settings, or leave that language out of the request. Retrying the same request will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#language_not_enabled",
  "title": "Language not enabled",
  "status": 400,
  "detail": "Language(s) not enabled on this site: de",
  "instance": "/api/v1/sites/site_01example/starter-questions",
  "code": "language_not_enabled",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

sitemap_not_on_site

Status 400. The sitemapUrl you sent to start a sitemap import is not on the site's URL or one of its allowed origins. It is refused before any request is made to it, and no import starts. errors points at /sitemapUrl.

What to do: send a sitemap address on the site, or send no sitemapUrl and let the import look in robots.txt and the usual locations. Retrying the same address will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#sitemap_not_on_site",
  "title": "Sitemap not on the site",
  "status": 400,
  "detail": "The sitemap address is not on an address this site allows. No request was made to it.",
  "instance": "/api/v1/sites/site_01example/pages/discover",
  "code": "sitemap_not_on_site",
  "errors": [
    {
      "code": "sitemap_not_on_site",
      "detail": "Give a sitemap address on the site, or none.",
      "pointer": "/sitemapUrl"
    }
  ],
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

invalid_exclusion_rule

Status 400. One of the rules you sent to add exclusion rules is not a valid path pattern: it is empty, or it contains a space, a ? or a #, or it is longer than 200 characters. The whole request is refused and nothing is changed: no rule is added and no page is removed. errors holds one entry pointing at the rule at fault, with the code not_a_valid_rule or too_long.

A rule is a path without the domain, where * matches any run of characters, such as blogs/* or */cart. One leading / is accepted and removed.

What to do: fix or remove that rule, then send the request again.

{
  "type": "https://iadvize.ai/docs/api/errors#invalid_exclusion_rule",
  "title": "Invalid exclusion rule",
  "status": 400,
  "detail": "An exclusion rule is not a valid path pattern (a path without its query, such as blogs/*). Nothing was changed.",
  "instance": "/api/v1/sites/site_01example/pages/exclusions",
  "code": "invalid_exclusion_rule",
  "errors": [
    {
      "code": "not_a_valid_rule",
      "detail": "Fix or remove this rule.",
      "pointer": "/rules/1"
    }
  ],
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

not_found

Status 404. The resource does not exist, or it is not visible to your organization. A resource that belongs to another organization, was deleted, or sits under a different site reads exactly like one that never existed. A path that no route serves also gets this code.

What to do: check the ids in the path. Ids are opaque strings, so use them as the API returned them. A missing resource will not appear by retrying.

{
  "type": "https://iadvize.ai/docs/api/errors#not_found",
  "title": "Resource not found",
  "status": 404,
  "detail": "Site not found",
  "instance": "/api/v1/sites/site_01example",
  "code": "not_found",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

A method that an existing path does not support (a POST on a read-only path, say) is answered by the framework with a plain 405, not a problem document.

last_site

Status 409. The request conflicts with the current state of the resource. Today the only case is DELETE /api/v1/sites/{siteId} on the only site of your organization. An organization keeps at least one site. Nothing was changed.

What to do: create another site, then delete this one. Retrying the same request without adding a site will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#last_site",
  "title": "An organization keeps at least one site",
  "status": 409,
  "detail": "An organization keeps at least one site",
  "instance": "/api/v1/sites/site_01example",
  "code": "last_site",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

tag_check_unavailable

Status 409. POST /api/v1/sites/{siteId}/tag-check on a site whose tag monitoring is off, or that has no address to check. Nothing was loaded and nothing was changed.

What to do: switch tag monitoring on with tagMonitoringEnabled on Update a site, and make sure the site has a URL, then try again. Retrying the same request without doing so will fail the same way. A check made less than a minute after the last one is a different case: it answers 429 with a Retry-After header.

{
  "type": "https://iadvize.ai/docs/api/errors#tag_check_unavailable",
  "title": "Tag check unavailable",
  "status": 409,
  "detail": "Tag monitoring is off for this site, or the site has no address to check.",
  "instance": "/api/v1/sites/site_01example/tag-check",
  "code": "tag_check_unavailable",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

holdout_already_running

Status 409. POST /api/v1/sites/{siteId}/holdout/start on a site where a holdout test is already running. A site runs one test at a time. Nothing was created.

What to do: read the running test with Get a site's holdout test, and stop it if you want a new one. Retrying the same request will fail the same way until then.

{
  "type": "https://iadvize.ai/docs/api/errors#holdout_already_running",
  "title": "A holdout is already running",
  "status": 409,
  "detail": "A holdout is already running on this site",
  "instance": "/api/v1/sites/site_01example/holdout/start",
  "code": "holdout_already_running",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

holdout_not_running

Status 409. POST /api/v1/sites/{siteId}/holdout/stop on a site where no holdout test is running. Nothing was changed.

What to do: nothing, if you wanted the test stopped: it already is. Get the site's holdout test to see how the last one ended.

{
  "type": "https://iadvize.ai/docs/api/errors#holdout_not_running",
  "title": "No holdout is running",
  "status": 409,
  "detail": "No holdout is running on this site",
  "instance": "/api/v1/sites/site_01example/holdout/stop",
  "code": "holdout_not_running",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

holdout_measurement_unavailable

Status 409. POST /api/v1/sites/{siteId}/holdout/start on a site where the Shopify pixel is not recording add-to-carts. The sessions without the assistant can only add to the cart through the pixel, so a test started now would measure the pixel, not the assistant. Nothing was created. The detail tells two cases apart: add-to-cart counting is off for the site (the one in the example), or the pixel has recorded no add-to-cart on the site in the last 7 days ("The Shopify pixel has recorded no add-to-cart on this site in the last 7 days. Check the pixel, then start the test"). Both keep the same code, so branch on code, not on detail.

What to do: switch the counting on, or check the pixel, then start the test again. Retrying the same request before that will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#holdout_measurement_unavailable",
  "title": "The storefront measurement is not working",
  "status": 409,
  "detail": "Add-to-cart counting on your pages is off, so the Shopify pixel records nothing. Switch it on, then start the test",
  "instance": "/api/v1/sites/site_01example/holdout/start",
  "code": "holdout_measurement_unavailable",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

agent_experiment_already_running

Status 409. POST …/agents/{id}/experiments on an agent where an A/B test is already running. An agent runs one test at a time. Nothing was created.

What to do: read the running test with List an agent's experiments and stop it if you want a new one. Retrying the same request will fail the same way until then.

{
  "type": "https://iadvize.ai/docs/api/errors#agent_experiment_already_running",
  "title": "An experiment is already running on this agent",
  "status": 409,
  "detail": "An experiment is already running on this agent",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example/experiments",
  "code": "agent_experiment_already_running",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

holdout_experiment_running

Status 409. POST /api/v1/sites/{siteId}/holdout/start on a site where an engagement widget experiment is running. A holdout test and a widget experiment never run together on a site: the test serves half of the sessions no assistant, so the experiment would lose half of its traffic. Only a running experiment blocks a start; a paused or stopped one does not. Nothing was created.

What to do: stop the experiment in the dashboard, or let it end, then start the test. Widget experiments have no API, so this is done in the dashboard. Retrying the same request will fail the same way until then.

{
  "type": "https://iadvize.ai/docs/api/errors#holdout_experiment_running",
  "title": "A widget experiment is running",
  "status": 409,
  "detail": "A widget experiment is running on this site. Stop it or wait for it to end, then start the holdout",
  "instance": "/api/v1/sites/site_01example/holdout/start",
  "code": "holdout_experiment_running",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

agent_experiment_not_running

Status 409. POST …/experiments/{experimentId}/stop or …/promote named an experiment of the agent that already ended. Nothing was changed. An experiment id that is not this agent's is a 404 not_found instead.

What to do: nothing, if you wanted it stopped: it already is. Get the experiment to see how it ended (endedReason). Retrying the same request will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#agent_experiment_not_running",
  "title": "This experiment is not running",
  "status": 409,
  "detail": "No experiment is running on this agent",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example/experiments/agxp_01example/stop",
  "code": "agent_experiment_not_running",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

holdout_not_extendable

Status 409. POST /api/v1/sites/{siteId}/holdout/extend on a site whose test cannot be extended: no test is running, the running test has reached its planned end, or it has no planned end because it was started before planned ends existed. Nothing was changed.

What to do: get the site's holdout test to see its state, and start a new test if you want one. Retrying the same request will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#holdout_not_extendable",
  "title": "The holdout cannot be extended",
  "status": 409,
  "detail": "This holdout cannot be extended: it has ended, reached its planned end, or has no planned end",
  "instance": "/api/v1/sites/site_01example/holdout/extend",
  "code": "holdout_not_extendable",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

agent_experiment_refused

Status 409. POST …/agents/{id}/experiments on an agent that cannot be tested yet, or on a site that cannot measure purchases. Nothing was created. The problem carries a reason member that says which:

reasonMeaning
agent_not_testableThis type of agent cannot be tested (a custom agent).
agent_not_answeringOnly the agent that answers on its site can be tested.
no_championThe agent has no live version to test.
invalid_candidatecandidateVersionId is not another saved version of this agent.
candidate_not_readyThe candidate cannot go live yet (for example a connector it uses is gone), so it cannot be tested.
no_attribution_keyThe site has not issued a conversation join key yet, so purchases cannot be linked to conversations.
no_order_sourceThe site has recorded no order from a persistent source in the last 30 days.

Branch on reason, not on detail.

What to do: fix what the reason names, then send the request again. For agent_not_answering, see go-live. For candidate_not_ready, fix what go-live would refuse for that version, or choose another version. For the last two, see the conversation join key and Shopify order webhooks. Retrying unchanged will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#agent_experiment_refused",
  "title": "This experiment cannot start",
  "status": 409,
  "detail": "This site has recorded no recent order, so purchases cannot be measured",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example/experiments",
  "code": "agent_experiment_refused",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11",
  "reason": "no_order_source"
}

site_limit_reached

Status 409. POST /api/v1/sites on an organization that already holds as many sites as its site quota allows. The quota is set per organization by iAdvize (2 by default) and only limits adding a site: existing sites are never removed. Nothing was changed.

What to do: contact iAdvize to raise the quota, then create the site. Retrying the same request will fail the same way until then.

{
  "type": "https://iadvize.ai/docs/api/errors#site_limit_reached",
  "title": "Site limit reached",
  "status": 409,
  "detail": "The organization has reached its site limit",
  "instance": "/api/v1/sites",
  "code": "site_limit_reached",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

agent_answers_on_site

Status 409. The request would leave the site without the agent that answers on it: the agent the web tag and the AI Storefront serve. Nothing was changed. Two requests get this code:

  • DELETE /api/v1/sites/{siteId}/agents/{id} on the agent that answers on its site. A site that has an answering agent keeps it until you change it, so the agent can't be deleted on its own.
  • A deprecated status write on the version it answers with: PATCH /api/v1/sites/{siteId}/agents/{id}/versions/{versionId} moving that version away from the legacy champion status. The status writes can't stop the answering agent from answering.

For the delete, the detail tells two cases apart. When the site has other agents, it reads "This agent answers on its site. Make another agent answer before deleting it". When the agent is the site's only one, it reads "This agent answers on its site and is its only agent. Create another agent and make it answer before deleting this one". Both keep the same code, so branch on code, not on detail.

What to do: to stop the agent answering, call POST /api/v1/sites/{siteId}/agents/{id}/take-offline. To serve another version, or another agent, call POST /api/v1/sites/{siteId}/agents/{id}/go-live. Then delete the agent, if that's what you were doing. If it's the site's only agent, create another agent and put it live first. In the dashboard, the same gestures are Go live and Take offline… (see Put an agent live or take it offline). Retrying the same request before that will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#agent_answers_on_site",
  "title": "This agent answers on its site",
  "status": 409,
  "detail": "This agent answers on its site. Make another agent answer before deleting it",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example",
  "code": "agent_answers_on_site",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

version_not_ready

Status 409. A request would put in front of shoppers a version that can't go live yet. Three requests get this code:

errors holds one entry per unmet condition, each with a code and a detail worded as the fix, and no pointer or parameter:

codeMeaning
no_site_languageThe site has no enabled language.
connector_unavailableA connector the version uses was deleted.
instruction_block_unavailableAn instruction block the version uses was deleted.

Nothing was changed: the agent that answered before still answers, with the same version.

What to do: enable a language on the site, or create a new version without the deleted connector or instruction block and put that one live. Retrying the same version will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#version_not_ready",
  "title": "This version is not ready",
  "status": 409,
  "detail": "This version is not ready to go live",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example/go-live",
  "code": "version_not_ready",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11",
  "errors": [
    {
      "code": "connector_unavailable",
      "detail": "A connector this version uses was deleted. Start a new version without it."
    }
  ]
}

agent_live_changed

Status 409. Two POST …/agents/{id}/go-live calls on the same agent ran at the same time, and this one lost: the other one put its version live first, so this call changed nothing.

What to do: check what is live now (list the agent's versions: the one shoppers get has lifecycleStatus live; the MCP list_sites tool gives each agent a status of live or offline), then retry if you still want this version live. A retry with the version that is already live changes nothing. In the dashboard, the same race reads "Someone put this agent live at the same time. Reload the page and try again."

{
  "type": "https://iadvize.ai/docs/api/errors#agent_live_changed",
  "title": "This agent went live at the same time",
  "status": 409,
  "detail": "Another go-live of this agent ran at the same time, so this one changed nothing. Read the agent again and retry.",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example/go-live",
  "code": "agent_live_changed",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

agent_not_answering

Status 409. POST …/agents/{id}/take-offline named an agent that doesn't answer on its site, so there is nothing to take offline. Nothing was changed.

What to do: nothing, if you wanted the agent offline: it already is. The REST API has no read for which agent answers yet; the MCP list_sites tool gives each agent a status of live or offline. Retrying the same request will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#agent_not_answering",
  "title": "This agent does not answer on its site",
  "status": 409,
  "detail": "This agent does not answer on its site",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example/take-offline",
  "code": "agent_not_answering",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

version_locked

Status 409. POST …/agents/{id}/versions with target.mode append named a version that can no longer change: it has been live, or it is part of an A/B test, so it is kept as it is. The detail says which. Nothing was saved.

What to do: send the same changes as a new version, with target.mode new (the default). To check before you save, read the version: a locked one has locked true, and lockedReason says why (live). In the dashboard this is Save as a new version (see Locked versions). Retrying the same request will fail the same way.

{
  "type": "https://iadvize.ai/docs/api/errors#version_locked",
  "title": "This version can no longer change",
  "status": 409,
  "detail": "This version has been live, so it is kept as it is. Save the changes as a new version (target mode new).",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example/versions",
  "code": "version_locked",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

version_changed

Status 409. POST …/agents/{id}/versions with target.mode append sent an expectedHeadId that is no longer the latest saved state of that version: someone saved on it since you read it. Nothing was saved.

The problem says when the latest save on the version was made and through which channel, never by whom:

  • savedAt: when the latest saved state of the version was saved (ISO 8601), or null when it is not known.
  • savedByType: member (a member of your organization), api_key (an API key) or system (iAdvize itself). It is null when the channel is not on record, for example in the moments right after the save.

What to do: read the version again (GET …/agents/{id}/versions), then retry with its latest saved state as expectedHeadId. Or keep your changes apart by saving them as a new version (target.mode new).

{
  "type": "https://iadvize.ai/docs/api/errors#version_changed",
  "title": "This version changed since you read it",
  "status": 409,
  "detail": "Someone saved on this version since you read it. Read the version again and retry with its latest saved state as expectedHeadId, or save the changes as a new version.",
  "instance": "/api/v1/sites/site_01example/agents/agt_01example/versions",
  "code": "version_changed",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11",
  "savedAt": "2026-10-21T09:14:03.512Z",
  "savedByType": "member"
}

discovery_already_running

Status 409. A sitemap import of this site is already running, so a new one did not start. A site runs one import at a time. An import that has made no progress for 30 minutes is closed as failed and stops blocking.

What to do: read the running import with List a site's sitemap imports or Get a sitemap import, and start another once its state is no longer running.

{
  "type": "https://iadvize.ai/docs/api/errors#discovery_already_running",
  "title": "A sitemap import is already running",
  "status": 409,
  "detail": "A sitemap import of this site is already running. Start another once it has ended.",
  "instance": "/api/v1/sites/site_01example/pages/discover",
  "code": "discovery_already_running",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

exclusion_rule_limit_reached

Status 409. Adding these rules would leave the site with more than 50 exclusion rules. Nothing was changed: no rule is added and no page is removed. Rules the site already holds don't count twice.

What to do: list the rules with List a site's exclusions (kind=rule), delete the ones you no longer need, then send the request again.

{
  "type": "https://iadvize.ai/docs/api/errors#exclusion_rule_limit_reached",
  "title": "Exclusion rule limit reached",
  "status": 409,
  "detail": "A site keeps at most 50 exclusion rules. Remove one first.",
  "instance": "/api/v1/sites/site_01example/pages/exclusions",
  "code": "exclusion_rule_limit_reached",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

wildcard_question_limit_reached

Status 409. The site already holds 100 starter questions with a * wildcard URL pattern, switched on or off, and the request would write one more. It applies to creating, updating and duplicating a question. Questions with exact patterns (no *) and questions that target page types don't count. Nothing was changed.

What to do: list the site's starter questions, delete the wildcard ones you no longer need or rewrite them with exact patterns, then send the request again. Retrying the same request will fail the same way until then. The MCP create_starter_questions and update_starter_questions tools report the same refusal as a rejected item with the reason wildcard_question_limit_reached.

{
  "type": "https://iadvize.ai/docs/api/errors#wildcard_question_limit_reached",
  "title": "Wildcard question limit reached",
  "status": 409,
  "detail": "A site holds at most 100 questions with a wildcard pattern",
  "instance": "/api/v1/sites/site_01example/starter-questions",
  "code": "wildcard_question_limit_reached",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

rate_limited

Status 429. You sent too many requests. The response carries a Retry-After header with the number of seconds to wait.

What to do: stop sending requests for Retry-After seconds, then retry. Do not retry in a tight loop. See Rate limits.

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 60
{
  "type": "https://iadvize.ai/docs/api/errors#rate_limited",
  "title": "Too many requests",
  "status": 429,
  "detail": "Too many requests. Wait before retrying.",
  "instance": "/api/v1/sites",
  "code": "rate_limited",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

generation_limit_reached

Status 429. The site has used its daily ceiling of generated pages, or the organization has used its monthly quota in capped mode, so a request for starter questions had nothing to start: every page in it was skipped for that reason. The response carries a Retry-After header with the number of seconds until the limit resets, at the next UTC midnight or on the 1st of the next month. No model was called.

What to do: wait Retry-After seconds, then ask again. A request where only some pages fit in the budget is not an error: it is answered 202, and the pages that did not fit are listed as skipped with the reason daily_limit_reached or monthly_limit_reached. The page list carries the budget in meta.generationBudget, so you can check what is left before you ask.

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 41520
{
  "type": "https://iadvize.ai/docs/api/errors#generation_limit_reached",
  "title": "Generation limit reached",
  "status": 429,
  "detail": "The limit of generated pages is reached",
  "instance": "/api/v1/sites/site_01example/pages/generate",
  "code": "generation_limit_reached",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

internal_error

Status 500. Something failed on our side. The detail is always generic and never contains an internal message.

What to do: retry a GET after a short delay, with the delay growing on each attempt. Before retrying a POST, PATCH or DELETE, read the resource to check whether the first attempt went through. If the error persists, contact support and quote the requestId.

{
  "type": "https://iadvize.ai/docs/api/errors#internal_error",
  "title": "Internal server error",
  "status": 500,
  "detail": "An unexpected error occurred.",
  "instance": "/api/v1/sites",
  "code": "internal_error",
  "requestId": "req_2f6c1d5e-7a7e-4f4b-9b39-1d1c8a7f0c11"
}

Rate limits

Two limits apply, and both are checked before your API key is validated:

  • Per API key. Requests sent with the same key share one allowance.
  • Per client address. Requests from the same IP address share one allowance, whatever key they use.

Both are counted over a 60-second window. The API reference intro currently states 600 requests per minute per key and 1200 requests per minute per client address. Those figures are starting values and can change without a new API version. Do not hard-code them: read the Retry-After header of a 429 and wait that many seconds. The API currently always answers 60.

The address limit is checked first, so a burst of requests from one address can get a 429 even when your key is well under its own limit.

API changes within v1

The API is additive within /v1. New fields, new error codes and new message block types can appear without a new version. Write clients that ignore fields, codes and block types they do not know.

Deprecated calls

A call that is being replaced keeps working, and every answer to it carries headers that say so:

  • Deprecation: when it was deprecated (RFC 9745), as @ followed by Unix seconds.
  • Link: the call that replaces it, with rel="successor-version".
  • Sunset: when it stops working (RFC 8594), as an HTTP date. Only sent once that date is set.

Two writes are deprecated today, both in favour of POST /api/v1/sites/{siteId}/agents/{id}/go-live:

Both stop working on 27 April 2027, and every answer carries Sunset with that date until then. Move to the go-live call before it.

On this page