{
  "openapi": "3.1.0",
  "info": {
    "title": "Zapped managed-profile provisioning API",
    "version": "2026-09-22",
    "description": "Owner-scoped machine access for private managed-profile drafts, bounded imports, owner review, and approved publication. Access requires a customer grant, enabled deployment switch, and bearer credential with the operation scope. The reference is public while machine access is enabled on the deployment. Reading it requires no account grant; executing operations does. This is not a general availability or pricing claim.",
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-Proprietary"
    }
  },
  "servers": [
    {
      "url": "/api/managed-profiles",
      "description": "Bearer-only REST transport. JSON requests are limited to 262144 bytes; media files to 7340032 bytes (7 MB), or the server's lower upload limit, which a 413 reports as limit_bytes. The per-key quota is 60 requests per UTC minute."
    }
  ],
  "security": [
    {
      "bearerCredential": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerCredential": {
        "type": "http",
        "scheme": "bearer",
        "description": "An owner-scoped managed-profile credential (`zpk_` + 48 hex characters). The same scoped customer API key system serves the account API, managed-profile REST and MCP, subject to their respective grants and scopes. Only its SHA-256 hash is stored. Revocation and expiry reject new requests. Accepted import work continues after expiry; revocation prevents further row/photo application and records cancellation in background batches. Completed results are retained."
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": [
          "success",
          "api_version"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "api_version": {
            "type": "string",
            "const": "2026-09-22"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "success",
          "api_version",
          "error"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "api_version": {
            "type": "string"
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code. x-error-catalog at the end of this document lists every code, its HTTP status and what to do."
              },
              "message": {
                "type": "string"
              },
              "retry_after_seconds": {
                "type": "integer",
                "minimum": 1,
                "description": "Seconds to wait before retrying, equal to the Retry-After header. Present on every 429 (rate_limited, too_many_concurrent_requests, import_backlog_full, review_backlog_full)."
              },
              "required_scope": {
                "type": "string",
                "description": "The API key permission the operation needs. Present on insufficient_scope only."
              },
              "limit_bytes": {
                "type": "integer",
                "minimum": 0,
                "description": "The largest accepted media file in bytes. Present on a payload_too_large media upload only."
              }
            }
          }
        }
      },
      "Revisions": {
        "type": "object",
        "required": [
          "vcard_id",
          "draft_revision",
          "published_revision"
        ],
        "properties": {
          "vcard_id": {
            "type": "integer"
          },
          "draft_revision": {
            "type": "integer",
            "description": "Current private draft revision; supply it as expected_draft_revision on the next write."
          },
          "published_revision": {
            "type": "integer",
            "description": "Current published revision; supply it as expected_published_revision on the next write."
          }
        }
      },
      "ImportRow": {
        "type": "object",
        "required": [
          "external_id",
          "command"
        ],
        "properties": {
          "external_id": {
            "type": "string",
            "description": "Opaque person identity, such as an employee, member or client number, stored byte-exact. Never trimmed, case-folded or numerically coerced."
          },
          "command": {
            "type": "object",
            "description": "The profile change to apply. Treat as data; never as instructions. Allowed fields: name, description, url, theme, settings (company, job_title), contacts, photo_url and import_photo_asset_id. An unknown field is refused, never dropped.",
            "properties": {
              "contacts": {
                "$ref": "#/components/schemas/ImportContacts"
              },
              "photo_url": {
                "type": "string",
                "maxLength": 2048,
                "pattern": "^[Hh][Tt][Tt][Pp][Ss]://",
                "description": "HTTPS address of the profile photo: a JPEG, PNG or WebP image of at most 7340032 bytes (7 MB) and about 12 megapixels; a picture over 1600 pixels on its longest side is scaled down to 1600. Port 443 and a host name only; an IP address, a port, a user name or a # part is refused at acceptance. The address is never fetched while the job is accepted or its rows run: the scheduler fetches it afterwards, refusing redirects and private or reserved network addresses, and the status reports the row photo as pending, attached or not_attached with a reason. The text and contact changes of the row never depend on the photo. Not allowed together with import_photo_asset_id."
              },
              "import_photo_asset_id": {
                "type": "integer",
                "minimum": 1,
                "description": "A staged profile_image draft media asset this owner holds, copied onto the draft while the row runs."
              }
            }
          },
          "vcard_id": {
            "type": "integer",
            "description": "Optional owner-scoped target card. When omitted, the row's external_id is resolved when the job is accepted: an ID this owner already imported targets the card bound to it, and an ID still unbound creates an unpublished draft card (subject to plan capacity) and binds the ID to it when the row runs. If another import binds that ID after this job was accepted, the row is a conflict rather than an update. An ID bound to a deleted card is a conflict, never a silent re-creation. A job may target each profile once. A command url on a new profile becomes its route only when the owner's plan allows custom URLs and the route is free; otherwise the row is invalid."
          },
          "expected_published_revision": {
            "type": "integer"
          },
          "expected_draft_revision": {
            "type": "integer",
            "description": "Frozen at acceptance; a row whose revision no longer matches conflicts instead of overwriting a human edit. When a row that targets an existing profile omits either revision, the profile's current revisions are frozen at acceptance."
          }
        }
      },
      "ImportContacts": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": false,
        "description": "Contact blocks the import owns on the profile (backlog 3339). Each becomes an email, phone or link block checked with the block editor's rules. The blocks carry an import marker, so a reimport updates them in place and never duplicates them, and blocks the owner added by hand are never read, changed or removed. A field left out keeps its current block; an import never removes a contact block. A new block counts against the plan's block limit, and a row that would go over it is invalid.",
        "properties": {
          "email": {
            "type": "string",
            "maxLength": 320,
            "description": "Email address."
          },
          "phone": {
            "type": "string",
            "maxLength": 64,
            "description": "3 to 20 digits with an optional leading +. Spaces, dashes and brackets are removed before it is stored."
          },
          "website": {
            "type": "string",
            "maxLength": 1024,
            "description": "Address starting with https:// or http://. Blocked domains are refused, and Safe Browsing is checked when the site enables it."
          }
        }
      }
    },
    "parameters": {
      "ApiVersion": {
        "name": "Zapped-Api-Version",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "const": "2026-09-22"
        },
        "description": "Required on every request. A missing or different version is 400 unsupported_version."
      }
    },
    "headers": {
      "RateLimit-Limit": {
        "description": "The key's request quota per UTC minute (60). Sent on every response to a request that passed authentication and quota admission.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left for this key in the current UTC minute. It is 0 on a rate_limited refusal.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the key's minute quota resets.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying. Sent with every 429: until the quota window resets for rate_limited, 2 for too_many_concurrent_requests, 60 for import_backlog_full and 3600 for review_backlog_full. error.retry_after_seconds carries the same value.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Typed failure. A request refused after quota admission still carries the RateLimit headers; one refused earlier (bad version, body or credential) does not.",
        "headers": {
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Retry after the indicated interval. error.code rate_limited: the credential exhausted its UTC minute quota (60), its 10-second burst window (15), or the owner's minute total across all keys (120). error.code too_many_concurrent_requests: too many requests are in flight for this owner (2) or this server (6); retry after 2 seconds. error.code import_backlog_full (import submission only): accepting these rows would leave more than 2000 import rows waiting for this account; retry after 60 seconds. error.code review_backlog_full (review requests only): the account already has 50 review requests waiting for the owner; retry after 3600 seconds, or ask the owner to decide the pending reviews first. Every 429 carries Retry-After and error.retry_after_seconds. RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset are sent whenever the minute quota was read, which is every 429 except too_many_concurrent_requests.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "The service cannot take the request right now. error.code machine_access_disabled: managed profile API access is switched off for this deployment; retrying will not help until it is switched on. error.code quota_unavailable: the request quota could not be checked, so the request was refused rather than admitted unmetered; retry with exponential backoff starting at a few seconds. error.code service_unavailable: credential storage is unavailable; retry with backoff. A 503 carries neither Retry-After nor RateLimit headers.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/profiles/open": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Open a profile draft",
        "description": "Requires scope vcards:write. A nonexistent card and another owner's card return the same denial, so this operation cannot be used to enumerate which card ids exist.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "vcard_id"
                ],
                "properties": {
                  "vcard_id": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft opened",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Revisions"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "405": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error",
            "description": "invalid_request: vcard_id is missing or not a positive integer."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "openManagedProfile"
      }
    },
    "/profiles/autosave": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Save a profile draft revision",
        "description": "Requires scope vcards:write. Both expected revisions are mandatory: a stale or replayed revision returns 409 rather than overwriting a newer human edit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "vcard_id",
                  "expected_draft_revision",
                  "expected_published_revision",
                  "payload"
                ],
                "properties": {
                  "vcard_id": {
                    "type": "integer"
                  },
                  "expected_draft_revision": {
                    "type": "integer"
                  },
                  "expected_published_revision": {
                    "type": "integer"
                  },
                  "payload": {
                    "type": "object",
                    "additionalProperties": false,
                    "description": "Only name, description, theme and settings may be sent. Only the fields you send change. Code, address, domain, pixels, branding, visibility and redirect settings stay with the owner's own editor and are refused with 422 unsupported_field. Text that contains characters which hide or reorder it is refused with 422 invalid_characters.",
                    "properties": {
                      "name": {
                        "type": "string",
                        "maxLength": 256
                      },
                      "description": {
                        "type": "string",
                        "maxLength": 512
                      },
                      "theme": {
                        "type": "string"
                      },
                      "settings": {
                        "type": "object",
                        "description": "Editable settings such as company, job_title, first_name, last_name, title, fonts and background. leap_link, pwa_* and outbound_links_follow_is_enabled are refused."
                      }
                    }
                  },
                  "partial": {
                    "type": "boolean",
                    "description": "Accepted for compatibility. An integration's autosave always keeps every field it does not send."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft saved",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Revisions"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "409": {
            "$ref": "#/components/responses/Error",
            "description": "Stale revision or a conflicting draft"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "Server error"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "saveManagedProfileDraft"
      }
    },
    "/imports/preview": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Validate a CSV import and report per-row outcomes",
        "description": "Requires scope import:write. Reports what would happen and reserves nothing: no job, row, identity or capacity is created. Mappable fields: external_id (required), name, description, url, theme, company, job_title, email, phone, website and photo. Email, phone and website cells are checked with the block editor's rules; an empty cell keeps the current block. A photo cell that is an address (https://) is checked with the fetch policy but never fetched here; any other photo value is matched against the photos map, and a referenced photo is accepted only when it belongs to this owner.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "headers",
                  "mapping",
                  "csv"
                ],
                "properties": {
                  "headers": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "mapping": {
                    "type": "object",
                    "description": "field -> zero-based column index; must include external_id. Fields: external_id, name, description, url, theme, company, job_title, email, phone, website, photo."
                  },
                  "csv": {
                    "type": "string"
                  },
                  "photos": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "description": "Photo cell value to the ID of a draft media asset the account owner already holds. Other values are ignored."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-row outcomes",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "counts": {
                              "type": "object"
                            },
                            "rows": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "ordinal": {
                                    "type": "integer"
                                  },
                                  "external_id": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "outcome": {
                                    "type": "string",
                                    "description": "mapped, invalid, duplicate or missing_photo."
                                  },
                                  "detail": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Plain reason for a row that is not mapped."
                                  },
                                  "photo_address": {
                                    "type": "boolean",
                                    "description": "True when the row names its photo by an address the scheduler will fetch after the row runs."
                                  },
                                  "photo_requires_attachment": {
                                    "type": "boolean",
                                    "description": "True when the row names a staged upload that is copied while the row runs."
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "Server error"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "previewManagedProfileImport"
      }
    },
    "/imports": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Accept an import job",
        "description": "Requires scope import:write. Returns a job id only after a confirmed commit. Retry semantics: the same (owner, operation, retry_key) with the same input digest returns the existing job (200); a different digest under the same key is a conflict (409) and alters nothing; another owner may reuse the same key independently. A retry key is not a JSON-RPC id and must not be replaced to force a fresh job after a lost response.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "retry_key",
                  "rows"
                ],
                "properties": {
                  "retry_key": {
                    "type": "string"
                  },
                  "rows": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ImportRow"
                    }
                  }
                }
              },
              "example": {
                "retry_key": "example-import-001",
                "rows": [
                  {
                    "external_id": "person-001",
                    "command": {
                      "name": "Ada Example"
                    }
                  },
                  {
                    "external_id": "person-002",
                    "command": {
                      "name": "Riley Example",
                      "contacts": {
                        "email": "riley@example.com",
                        "phone": "+15550100102",
                        "website": "https://www.example.com"
                      },
                      "photo_url": "https://photos.example.com/riley.jpg"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay of an existing job",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "job_id": {
                              "type": "integer"
                            },
                            "status": {
                              "type": "string",
                              "description": "The job's current status on a replay: accepted, running, complete, cancelled or aborted."
                            },
                            "created": {
                              "type": "boolean",
                              "const": false
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "New job accepted",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "job_id": {
                              "type": "integer"
                            },
                            "status": {
                              "type": "string"
                            },
                            "created": {
                              "type": "boolean",
                              "const": true
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error",
            "description": "retry_key_reused: the retry key was already used with different input. Nothing was changed."
          },
          "422": {
            "description": "invalid_request: a row, reference or the manifest is invalid. When rows are at fault, error.row_errors lists each one by its zero-based position in rows, with its external_id when readable, and nothing is accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "object",
                          "properties": {
                            "row_errors": {
                              "type": "array",
                              "maxItems": 50,
                              "items": {
                                "type": "object",
                                "required": [
                                  "row",
                                  "message"
                                ],
                                "properties": {
                                  "row": {
                                    "type": "integer",
                                    "minimum": 0
                                  },
                                  "external_id": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "message": {
                                    "type": "string"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "Server error"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "submitManagedProfileImport"
      }
    },
    "/imports/status": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Read an import job and its per-row results",
        "description": "Requires scope import:write. Returns the job status and, per row in submission order, its outcome (created, updated, unchanged, invalid, conflict, cancelled or pending) and profile ID. Another owner's job and a missing job both answer 404. Each row also reports its photo address state (photo and photo_detail), and photos counts them.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "job_id"
                ],
                "properties": {
                  "job_id": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "job_id": {
                              "type": "integer"
                            },
                            "status": {
                              "type": "string"
                            },
                            "row_count": {
                              "type": "integer"
                            },
                            "pending": {
                              "type": "integer"
                            },
                            "paused": {
                              "type": "boolean",
                              "description": "True while managed profile drafts are turned off. Pending rows keep their place and run when drafts are turned back on; status stays accepted or running."
                            },
                            "rows": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "external_id": {
                                    "type": "string"
                                  },
                                  "outcome": {
                                    "type": "string"
                                  },
                                  "vcard_id": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "detail": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Why the row was not applied as submitted, for example a stale revision or a photo that could not be attached. Null when there is nothing to explain."
                                  },
                                  "photo": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "enum": [
                                      null,
                                      "pending",
                                      "attached",
                                      "not_attached"
                                    ],
                                    "description": "The row's photo address: null when the row named none, pending until the scheduler has fetched it (usually within a few minutes), attached, or not_attached with the reason in photo_detail. A photo that is not attached never undoes the row's text and contact changes."
                                  },
                                  "photo_detail": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Why the photo was not attached, for example a redirect, a private network address, a file over 7 MB or a file that is not a JPEG, PNG or WebP image. Null otherwise."
                                  }
                                }
                              }
                            },
                            "photos": {
                              "type": "object",
                              "description": "Photo address counts over the reported rows.",
                              "properties": {
                                "pending": {
                                  "type": "integer"
                                },
                                "attached": {
                                  "type": "integer"
                                },
                                "not_attached": {
                                  "type": "integer"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error",
            "description": "The job or review id is missing or malformed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "Server error"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "getManagedProfileImportJob"
      }
    },
    "/profiles/media": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Add a headshot, logo or cover image to a draft",
        "description": "Requires scope vcards:write. multipart/form-data with vcard_id, role (avatar, logo or cover), both expected revisions and file. The file limit is 7340032 bytes (7 MB), or the server's lower upload limit, which a 413 reports as limit_bytes; each role's own type and dimension rules still apply. With media framing on, the image is prepared and applied with the editor's default framing in one request. Advances the draft revision by one.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "vcard_id",
                  "role",
                  "expected_draft_revision",
                  "expected_published_revision",
                  "file"
                ],
                "properties": {
                  "vcard_id": {
                    "type": "integer"
                  },
                  "expected_draft_revision": {
                    "type": "integer"
                  },
                  "expected_published_revision": {
                    "type": "integer"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "avatar",
                      "logo",
                      "cover"
                    ]
                  },
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "vcard_id": {
                              "type": "integer"
                            },
                            "role": {
                              "type": "string"
                            },
                            "draft_revision": {
                              "type": "integer"
                            },
                            "published_revision": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "Server error. On profiles/media, commit_outcome_unknown means the upload may or may not have been applied: reopen the profile to check."
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "uploadManagedProfileMedia"
      }
    },
    "/profiles/blocks": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Create, update or delete a contact, link, social or video block",
        "description": "Requires scope vcards:write. Allowed types: link, email, phone, address, the social networks, youtubevideo and savecontact. Custom HTML, lead forms and payment links stay in the dashboard. A create with a client_key already in the draft is acknowledged without a second block, even with the old revision. Update and delete name the block by vcard_block_id or client_key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "operation",
                  "vcard_id",
                  "expected_draft_revision",
                  "expected_published_revision"
                ],
                "properties": {
                  "vcard_id": {
                    "type": "integer"
                  },
                  "expected_draft_revision": {
                    "type": "integer"
                  },
                  "expected_published_revision": {
                    "type": "integer"
                  },
                  "operation": {
                    "type": "string",
                    "enum": [
                      "create",
                      "update",
                      "delete"
                    ]
                  },
                  "type": {
                    "type": "string"
                  },
                  "client_key": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{1,64}$"
                  },
                  "vcard_block_id": {
                    "type": "integer"
                  },
                  "name": {
                    "type": "string"
                  },
                  "value": {
                    "type": "string"
                  },
                  "is_enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "vcard_id": {
                              "type": "integer"
                            },
                            "draft_revision": {
                              "type": "integer"
                            },
                            "published_revision": {
                              "type": "integer"
                            },
                            "client_key": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "Server error"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "mutateManagedProfileBlock"
      }
    },
    "/profiles/reviews": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Ask the owner to review exact draft revisions",
        "description": "Requires scope vcards:write. Records a review of up to 25 profiles at their current revisions and returns review_url, the owner-only dashboard page where the owner sees a preview of each exact revision and approves or rejects it. A credential cannot approve. A stale revision is 409; a foreign and a missing card are the same 403. Retrying an identical pending request with the same credential returns the existing review and HTTP 200. A changed, decided, expired, or stale request can create a new review. An optional retry_key binds a retry to the first call: the same key with the same items returns that review (HTTP 200, created false) whatever happened to it since, and the same key with different items is 409 retry_key_reused. A key is scoped to the account and is free again once its review is purged, 90 days after it expired.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items"
                ],
                "properties": {
                  "retry_key": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128,
                    "description": "Optional. Reuse unchanged for retries of this exact request."
                  },
                  "items": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 25,
                    "items": {
                      "type": "object",
                      "required": [
                        "vcard_id",
                        "expected_draft_revision",
                        "expected_published_revision"
                      ],
                      "properties": {
                        "vcard_id": {
                          "type": "integer"
                        },
                        "expected_draft_revision": {
                          "type": "integer"
                        },
                        "expected_published_revision": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "items": [
                  {
                    "vcard_id": 123,
                    "expected_draft_revision": 1,
                    "expected_published_revision": 0
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Identical pending review reused for this credential and exact revision set.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "review_id": {
                              "type": "string"
                            },
                            "review_url": {
                              "type": "string",
                              "format": "uri"
                            },
                            "created": {
                              "type": "boolean",
                              "const": false
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "New owner review requested.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "review_id": {
                              "type": "string"
                            },
                            "review_url": {
                              "type": "string",
                              "format": "uri"
                            },
                            "expires_at": {
                              "type": "string"
                            },
                            "items": {
                              "type": "array"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "Server error"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "requestManagedProfileReview"
      }
    },
    "/profiles/reviews/status": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Read the owner's decisions",
        "description": "Requires scope vcards:write. Per profile: pending, approved, rejected, stale (the draft changed after the request), expired or published.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "review_id"
                ],
                "properties": {
                  "review_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "review_id": {
                              "type": "string"
                            },
                            "expires_at": {
                              "type": "string"
                            },
                            "expired": {
                              "type": "boolean"
                            },
                            "items": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "vcard_id": {
                                    "type": "integer"
                                  },
                                  "draft_revision": {
                                    "type": "integer"
                                  },
                                  "published_revision": {
                                    "type": "integer"
                                  },
                                  "decision": {
                                    "type": "string"
                                  },
                                  "published": {
                                    "type": "boolean"
                                  },
                                  "result_published_revision": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error",
            "description": "The job or review id is missing or malformed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "Server error"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "getManagedProfileReview"
      }
    },
    "/profiles/publish": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/ApiVersion"
          }
        ],
        "summary": "Publish exactly the revision the owner approved",
        "description": "Requires scope publish:write and owner approval of the exact current revisions. The review receipt commits atomically with publication. Retrying the same request after an uncertain response returns HTTP 200 with already_published true and the stored published_revision, without republishing. An absent or stale approval returns 409.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "vcard_id",
                  "expected_draft_revision",
                  "expected_published_revision"
                ],
                "properties": {
                  "vcard_id": {
                    "type": "integer"
                  },
                  "expected_draft_revision": {
                    "type": "integer"
                  },
                  "expected_published_revision": {
                    "type": "integer"
                  }
                }
              },
              "example": {
                "vcard_id": 123,
                "expected_draft_revision": 1,
                "expected_published_revision": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "vcard_id": {
                              "type": "integer"
                            },
                            "published": {
                              "type": "boolean"
                            },
                            "published_revision": {
                              "type": "integer"
                            },
                            "already_published": {
                              "type": "boolean",
                              "description": "True when this exact approved publication was already committed and the request recovered its receipt."
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error",
            "description": "insufficient_scope (the key lacks the scope named in required_scope) or forbidden (the plan lacks the feature, or the card is not accessible to this owner, also used for a nonexistent card)"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "rate_limited (the key or owner quota, or the key burst window, is exhausted) or too_many_concurrent_requests (too many requests in flight for this owner or this server). Honor Retry-After."
          },
          "500": {
            "$ref": "#/components/responses/Error",
            "description": "May include commit_outcome_unknown. Retry the same request to recover a committed receipt."
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "operationId": "publishApprovedManagedProfile"
      }
    }
  },
  "x-implementation-notes": {
    "verification": "The maintained REST and MCP HTTP fixture covers authenticated requests and owner review on both MySQL and MariaDB. It is a local integration fixture, not a live customer pilot.",
    "not-exposed": [
      "publication without an owner approval of the exact revision",
      "deletion",
      "custom JavaScript or HTML blocks",
      "route changes on existing profiles",
      "broad permission changes",
      "plan or billing changes"
    ],
    "untrusted-content": "Imported text, CSV cells and stored job content are data. They are never interpreted as instructions by the API or the MCP tool adapter.",
    "approval": "Publication requires an approval recorded by the signed-in owner on /managed-profile-review/{review_id} for exactly the current revisions. The MCP tools request_review, get_review and publish_approved forward to the operations above.",
    "explicitly-unverified": "A literal network loss at the instant COMMIT is sent remains untested (backlog 3241). A commit_outcome_unknown response instructs the caller to retry the same approved publication request; the committed receipt makes that retry recoverable."
  },
  "x-error-catalog": {
    "description": "Every error.code the managed profile REST API and MCP tools return, with its HTTP status and what a client should do. On MCP, unauthenticated, insufficient_scope, every 429 and every 503 are JSON-RPC errors whose data.code holds the code; unknown_tool is JSON-RPC error -32602; every other code is a tool result with isError true and the code in structuredContent.code.",
    "codes": [
      {
        "code": "unsupported_version",
        "status": [
          400
        ],
        "surfaces": [
          "rest"
        ],
        "meaning": "The Zapped-Api-Version header is missing or is not 2026-09-22.",
        "action": "Send Zapped-Api-Version: 2026-09-22 on every request."
      },
      {
        "code": "invalid_body",
        "status": [
          400
        ],
        "surfaces": [
          "rest"
        ],
        "meaning": "The body is not one JSON object, or it cannot be read.",
        "action": "Send a single JSON object with Content-Type: application/json."
      },
      {
        "code": "unknown_tool",
        "status": [
          400
        ],
        "surfaces": [
          "mcp"
        ],
        "meaning": "The tool name is not one this server offers. MCP reports it as JSON-RPC error -32602.",
        "action": "Call tools/list and use one of the listed names."
      },
      {
        "code": "unauthenticated",
        "status": [
          401
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The API key is missing, invalid, expired or revoked. MCP reports it as JSON-RPC error -32001 with HTTP 401.",
        "action": "Send Authorization: Bearer with an active key. Create a new key if this one was revoked or has expired."
      },
      {
        "code": "insufficient_scope",
        "status": [
          403
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The key lacks the permission the operation needs; error.required_scope names it. MCP reports it as JSON-RPC error -32003 with HTTP 403 and a WWW-Authenticate challenge.",
        "action": "Use a key that holds that permission, or add it to this key on the API keys page."
      },
      {
        "code": "forbidden",
        "status": [
          403
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The plan does not include imports, the account is not active, or the card is not this account's. A card that does not exist answers the same way.",
        "action": "Check that the card ID belongs to the key's account and that the plan includes the feature. Retrying will not help."
      },
      {
        "code": "not_found",
        "status": [
          404
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The operation, import job or review does not exist for this account.",
        "action": "Check the path, job_id or review_id."
      },
      {
        "code": "method_not_allowed",
        "status": [
          405
        ],
        "surfaces": [
          "rest"
        ],
        "meaning": "The operation was called with a method other than POST.",
        "action": "Use POST."
      },
      {
        "code": "draft_conflict",
        "status": [
          409
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The draft changed after the revisions you sent, for example because a person edited it in the editor.",
        "action": "Open the profile again, check the current draft, and resend your change with the new revisions. Never overwrite blindly."
      },
      {
        "code": "published_revision_conflict",
        "status": [
          409
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The published card changed after the revisions you sent.",
        "action": "Open the profile again and resend your change with the new revisions."
      },
      {
        "code": "client_key_conflict",
        "status": [
          409
        ],
        "surfaces": [
          "rest"
        ],
        "meaning": "The block client_key already names a different block.",
        "action": "Use a new client_key for a new block, or update the block that already has this key."
      },
      {
        "code": "approval_conflict",
        "status": [
          409
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The owner's approval changed or expired, or the draft changed after it was approved.",
        "action": "Request a new review for the current revisions and wait for the owner to approve it."
      },
      {
        "code": "not_approved",
        "status": [
          409
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The owner has not approved these exact revisions.",
        "action": "Request a review, give review_url to the account owner, poll the review until the profile is approved, then publish those revisions."
      },
      {
        "code": "no_unpublished_changes",
        "status": [
          409
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "There is nothing new to publish for this profile.",
        "action": "Open the profile to read its current revisions. There is nothing to retry."
      },
      {
        "code": "payload_too_large",
        "status": [
          413
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The JSON body is over 256 KB, or the media file is over the upload limit (error.limit_bytes). MCP reports an oversized body as JSON-RPC error -32600 with HTTP 413.",
        "action": "Split the import into smaller requests, or send a smaller image."
      },
      {
        "code": "unsupported_media_type",
        "status": [
          415
        ],
        "surfaces": [
          "rest"
        ],
        "meaning": "The Content-Type is not application/json, or not multipart/form-data for a media upload.",
        "action": "Send the Content-Type the operation lists."
      },
      {
        "code": "invalid_request",
        "status": [
          400,
          422
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "A required field is missing, or a value has the wrong type, range or format; the message names it. MCP returns 400 only when the tool arguments are not an object.",
        "action": "Fix the field and send the request again. MCP clients can check arguments against the tool's inputSchema first."
      },
      {
        "code": "unsupported_field",
        "status": [
          422
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The request tries to change something an integration may not change: the profile address (url), custom code, pixels, domain, branding, or a setting that redirects or changes card behaviour.",
        "action": "Remove the field. The owner changes these in the editor."
      },
      {
        "code": "invalid_characters",
        "status": [
          422
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "A text field contains characters that hide or reorder text, such as bidirectional overrides, zero-width spaces or the byte order mark. The message names the field.",
        "action": "Remove those characters and send the text again."
      },
      {
        "code": "validation_failed",
        "status": [
          422
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The value failed the editor's own checks: an image of the wrong type or size, an invalid block value, or a publication check. The message says which.",
        "action": "Fix the value and send the request again."
      },
      {
        "code": "retry_key_reused",
        "status": [
          409
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "This retry_key was already used for an import or a review request with different input.",
        "action": "Use a new unique retry_key, such as a UUID, for new input; keys are shared by every integration of the account. Reuse a key only to retry exactly the same request."
      },
      {
        "code": "drafts_disabled",
        "status": [
          503
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "Managed profile drafts are turned off for this deployment, so no new import is accepted. An import already running pauses and resumes when drafts are turned back on.",
        "action": "Retry later. The import status reports paused: true while drafts are off."
      },
      {
        "code": "rate_limited",
        "status": [
          429
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The key's minute quota (60), its 10 second burst window (15), or the account's minute total across all keys (120) is used up. MCP reports it as JSON-RPC error -32002 with HTTP 429.",
        "action": "Wait the Retry-After seconds, then retry."
      },
      {
        "code": "too_many_concurrent_requests",
        "status": [
          429
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "Too many requests are in progress for this account (2) or this server (6).",
        "action": "Wait 2 seconds and retry. Send one request at a time per account."
      },
      {
        "code": "import_backlog_full",
        "status": [
          429
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "Accepting these rows would leave more than 2000 import rows waiting for this account.",
        "action": "Wait 60 seconds or until earlier imports finish, or send fewer rows."
      },
      {
        "code": "review_backlog_full",
        "status": [
          429
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The account already has 50 review requests waiting for the owner.",
        "action": "Ask the owner to decide the pending reviews, or retry after 3600 seconds."
      },
      {
        "code": "server_error",
        "status": [
          500
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The server could not complete the request.",
        "action": "Retry the same request after a short wait. Contact support if it keeps failing."
      },
      {
        "code": "commit_outcome_unknown",
        "status": [
          500
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The server could not confirm whether the write was saved.",
        "action": "For publication, retry the same request: a publication that was saved answers already_published. For media and blocks, open the profile and check its revisions before retrying."
      },
      {
        "code": "publish_failed",
        "status": [
          500
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The publication did not complete, and nothing was published.",
        "action": "Retry the same publish request."
      },
      {
        "code": "machine_access_disabled",
        "status": [
          503
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "Managed profile API access is switched off for this site. MCP reports every 503 as JSON-RPC error -32003 with HTTP 503.",
        "action": "Retrying will not help until access is switched on again. Contact support."
      },
      {
        "code": "quota_unavailable",
        "status": [
          503
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The request quota could not be checked, so the request was refused rather than let through unmetered. No Retry-After header is sent.",
        "action": "Retry with exponential backoff, starting at a few seconds."
      },
      {
        "code": "service_unavailable",
        "status": [
          503
        ],
        "surfaces": [
          "rest",
          "mcp"
        ],
        "meaning": "The API key storage is temporarily unavailable.",
        "action": "Retry with exponential backoff, starting at a few seconds."
      }
    ]
  }
}
