{
  "openapi": "3.1.0",
  "info": {
    "title": "My Content Tools Record Status API",
    "version": "0.9.0-preliminary",
    "summary": "Yes/no completeness status for a studio's per-performer scene records, for platforms the studio has connected.",
    "description": "PRELIMINARY. The API is not open yet and details may change before launch. No API keys are issued until the Public API Terms of Use are published at https://mycontenttools.com/developers.\n\nEvery request is server to server and carries three headers: x-mct-platform-key, x-mct-timestamp and x-mct-signature. The signature is the lowercase hex HMAC-SHA256, keyed with your signing secret, of these four lines joined by a single newline with no trailing newline: the uppercase METHOD, the request PATH (no host, no query string), the lowercase hex SHA-256 of the exact body bytes (of the empty string when there is no body), and the TIMESTAMP exactly as sent in x-mct-timestamp. The timestamp is UTC RFC 3339 (e.g. 2026-10-14T15:04:05Z) and must be within 300 seconds of the server clock. Any authentication failure answers 403 and never says which check failed. Worked signing examples with expected outputs are in test-vectors.json.\n\nRate limit: 30 requests per minute per key; in a batch each id counts as one request. Responses are never cacheable (Cache-Control: no-store).",
    "contact": { "email": "support@mycontenttools.com" }
  },
  "servers": [
    { "url": "https://{baseUrl}", "description": "The base URL is issued with your API key.", "variables": { "baseUrl": { "default": "issued-with-your-key.example" } } }
  ],
  "security": [ { "platformKey": [], "timestamp": [], "signature": [] } ],
  "paths": {
    "/v1/attestations/{recordId}": {
      "get": {
        "operationId": "getRecordStatus",
        "summary": "Get one scene record's status",
        "parameters": [
          { "name": "recordId", "in": "path", "required": true, "description": "The Record ID the studio gave you: one performer's record for one scene (a two-performer scene has two). Opaque, case-sensitive. Sign the path exactly as sent.", "schema": { "type": "string", "minLength": 1 } }
        ],
        "responses": {
          "200": { "description": "The record's status.", "headers": { "Cache-Control": { "$ref": "#/components/headers/NoStore" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Attestation" } } } },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/attestations:batch": {
      "post": {
        "operationId": "getRecordStatuses",
        "summary": "Get many scene records' statuses",
        "description": "Records that do not exist, or that you are not authorised to see, are left out of the array. A missing id means no answer for you, never incomplete. Ask for an id on its own to tell the cases apart.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchRequest" } } }
        },
        "responses": {
          "200": { "description": "Statuses for the ids you may see.", "headers": { "Cache-Control": { "$ref": "#/components/headers/NoStore" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/connection-codes": {
      "post": {
        "operationId": "createConnectionCode",
        "summary": "Create a one-time code a studio enters to connect your platform",
        "description": "Send no body (sign the SHA-256 of the empty string). Show the code to the studio; it works once and expires 15 minutes after it is created. The studio enters it in Model Release Pro and confirms your registered company name.",
        "responses": {
          "200": { "description": "A new connection code.", "headers": { "Cache-Control": { "$ref": "#/components/headers/NoStore" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConnectionCode" } } } },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "platformKey": { "type": "apiKey", "in": "header", "name": "x-mct-platform-key", "description": "Your API key." },
      "timestamp": { "type": "apiKey", "in": "header", "name": "x-mct-timestamp", "description": "Current UTC time, RFC 3339, within 300 seconds of the server clock." },
      "signature": { "type": "apiKey", "in": "header", "name": "x-mct-signature", "description": "Lowercase hex HMAC-SHA256 of METHOD\\nPATH\\nSHA256_HEX_OF_BODY\\nTIMESTAMP, keyed with your signing secret." }
    },
    "headers": {
      "NoStore": { "description": "Always no-store.", "schema": { "type": "string", "const": "no-store" } },
      "RetryAfter": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "minimum": 1 } }
    },
    "schemas": {
      "Attestation": {
        "type": "object",
        "additionalProperties": false,
        "required": ["recordId", "attestedAt", "fieldsComplete", "faceMatch", "schemaVersion"],
        "properties": {
          "recordId": { "type": "string", "description": "The Record ID you asked about." },
          "attestedAt": { "type": "string", "format": "date-time", "description": "When the record was checked, UTC, RFC 3339, second precision." },
          "fieldsComplete": { "type": "boolean", "description": "True when legal name, date of birth, verified government ID and the ID document are all on file." },
          "faceMatch": { "type": "null", "description": "Reserved. Always null for API Consumers." },
          "schemaVersion": { "type": "string", "const": "1" }
        }
      },
      "BatchRequest": {
        "type": "object",
        "required": ["recordIds"],
        "properties": {
          "recordIds": { "type": "array", "minItems": 0, "maxItems": 100, "items": { "type": "string", "minLength": 1 } }
        }
      },
      "BatchResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["attestations"],
        "properties": {
          "attestations": { "type": "array", "items": { "$ref": "#/components/schemas/Attestation" } }
        }
      },
      "ConnectionCode": {
        "type": "object",
        "additionalProperties": false,
        "required": ["code", "expiresAt"],
        "properties": {
          "code": { "type": "string", "pattern": "^MCT-[A-HJKMNP-Z2-9]{5}-[A-HJKMNP-Z2-9]{5}$", "examples": ["MCT-7Q4KX-R2PHM"] },
          "expiresAt": { "type": "string", "format": "date-time" }
        }
      },
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error"],
        "properties": {
          "error": { "type": "string", "enum": ["bad_request", "forbidden", "not_found", "method_not_allowed", "rate_limited", "internal_error"] }
        }
      }
    },
    "responses": {
      "BadRequest": { "description": "Malformed batch body, or more than 100 ids.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "bad_request" } } } },
      "Forbidden": { "description": "Authentication failed (unknown or unapproved key, bad signature, stale timestamp), or the studio has not connected your platform for this record.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "forbidden" } } } },
      "NotFound": { "description": "No scene record with this id, or an unknown path.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "not_found" } } } },
      "MethodNotAllowed": { "description": "Wrong method for the path.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "method_not_allowed" } } } },
      "RateLimited": { "description": "Over your rate limit.", "headers": { "Retry-After": { "$ref": "#/components/headers/RetryAfter" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "rate_limited" } } } },
      "InternalError": { "description": "Our fault. Retry later with backoff.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "internal_error" } } } }
    }
  }
}
