{
  "openapi": "3.1.0",
  "info": {
    "title": "Markovo Conversion API",
    "version": "2026-08-25",
    "description": "Account-metered file-to-Markdown conversion. Contract aligned 2026-08-25 with bounded guest previews, exactly-once settlement, delivery checks, and explicit Formula enhancement client defaults. Prices and capability status are discovered at runtime; failed jobs spend no Credits."
  },
  "servers": [{ "url": "https://markovo.net" }],
  "tags": [
    { "name": "Discovery" },
    { "name": "Conversion" },
    { "name": "Account" }
  ],
  "paths": {
    "/v1/capabilities": {
      "get": {
        "tags": ["Discovery"],
        "operationId": "listCapabilities",
        "responses": { "200": { "description": "Authoritative capability catalog", "content": { "application/json": { "schema": { "type": "object" } } } } }
      }
    },
    "/v1/catalog": {
      "get": {
        "tags": ["Discovery"],
        "operationId": "getCatalog",
        "responses": { "200": { "description": "Authoritative plan, pack, and Credit contract", "content": { "application/json": { "schema": { "type": "object" } } } } }
      }
    },
    "/v1/estimates": {
      "post": {
        "tags": ["Conversion"],
        "operationId": "createEstimate",
        "security": [{ "bearerAuth": [] }, {}],
        "requestBody": { "$ref": "#/components/requestBodies/ConversionUpload" },
        "responses": {
          "201": { "description": "Bounded estimate", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Estimate" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/url-estimates": {
      "post": {
        "tags": ["Conversion"],
        "operationId": "createUrlEstimate",
        "description": "Fetch one supported public source after explicit caller consent. Supports public HTML/text, Google Docs, Notion pages and bounded databases, GitHub text resources and discussions, Hacker News, and downloadable YouTube audio. No private/login-only sources, Tencent Docs account connection, browser rendering, generic binary downloads, or remote document images. Ordinary documents use URL fetch-time/word billing; YouTube uses audio-to-markdown duration billing (0.5 Credit/minute, minimum 0.3), at most one hour and 32 MB within the account upload limit. Anonymous estimates return job_token for the bounded guest-preview-session flow; authenticated callers confirm with JSON upload_id and a Credit ceiling at POST /v1/jobs. Guest downloads require sign-in, claim, and account settlement.",
        "security": [{ "bearerAuth": [] }, {}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["url", "accept_remote_fetch"],
                "properties": {
                  "url": { "type": "string", "maxLength": 2048, "description": "A public HTTPS link. The server normalizes bare domains and HTTP links to HTTPS; use a full HTTPS URL for portable clients. Never include credentials." },
                  "accept_remote_fetch": { "type": "boolean", "const": true }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Bounded public-source estimate; YouTube adds source_kind=youtube_audio and duration_ms. Anonymous requests also receive job_token.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Estimate" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/jobs": {
      "get": {
        "tags": ["Conversion"],
        "operationId": "listJobs",
        "security": [{ "bearerAuth": [] }],
        "responses": { "200": { "description": "Jobs owned by the current organization and project", "content": { "application/json": { "schema": { "type": "object", "properties": { "jobs": { "type": "array", "items": { "$ref": "#/components/schemas/Job" } } } } } } } }
      },
      "post": {
        "tags": ["Conversion"],
        "operationId": "confirmEstimate",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JobConfirmation" } } } },
        "responses": {
          "200": { "description": "Idempotent replay of an already confirmed job", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfirmedJob" } } } },
          "201": { "description": "New job queued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfirmedJob" } } } },
          "402": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/guest-preview-sessions": {
      "post": {
        "tags": ["Conversion"],
        "operationId": "startGuestPreviewSession",
        "security": [],
        "description": "Starts an ordered browser guest queue. Markovo converts only the longest continuous prefix whose cumulative immutable estimates are at most 5 Credits; it never skips an over-limit item.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestPreviewSessionInput" } } } },
        "responses": {
          "201": { "description": "Bounded guest preview queue started", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestPreviewSession" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "429": { "description": "The anonymous UTC-day preview was already used. No work is dispatched; the opaque staged recovery session lets a signed-in user continue the original queue after OAuth.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestPreviewRecoveryError" } } } },
          "409": { "$ref": "#/components/responses/Error" },
          "410": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/guest-preview-sessions/{session_id}/claim": {
      "parameters": [{ "$ref": "#/components/parameters/GuestPreviewSessionId" }],
      "post": {
        "tags": ["Conversion"],
        "operationId": "claimGuestPreviewSession",
        "security": [{ "cookieSession": [], "guestPreviewToken": [] }, { "oauthBearer": [], "guestPreviewToken": [] }],
        "description": "Binds completed anonymous preview jobs to the signed-in account and settles their measured Credit cost exactly once. An insufficient balance preserves the result and queue for retry after Billing.",
        "responses": {
          "200": { "description": "Already settled or newly settled exactly once", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GuestPreviewClaim" } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "402": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "410": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/convert": {
      "post": {
        "tags": ["Conversion"],
        "operationId": "convertWithCompatibilityShortcut",
        "security": [{ "bearerAuth": [] }, {}],
        "requestBody": { "$ref": "#/components/requestBodies/ConversionUploadWithMaximum" },
        "responses": {
          "200": { "description": "One or more jobs queued", "content": { "application/json": { "schema": { "type": "object" } } } },
          "207": { "description": "Only a subset of files were queued; unqueued files spend no Credits", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "402": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/jobs/{job_id}": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": {
        "tags": ["Conversion"],
        "operationId": "getJob",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "responses": { "200": { "description": "Owned job", "content": { "application/json": { "schema": { "type": "object", "properties": { "job": { "$ref": "#/components/schemas/Job" } } } } } }, "404": { "$ref": "#/components/responses/Error" } }
      },
      "delete": {
        "tags": ["Conversion"],
        "operationId": "deleteJob",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "responses": { "204": { "description": "Deleted" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/v1/jobs/{job_id}/download": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": {
        "tags": ["Conversion"],
        "operationId": "downloadResult",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "parameters": [{ "name": "format", "in": "query", "schema": { "type": "string", "enum": ["md", "delivery", "zip", "image"], "default": "md" } }, { "name": "asset_path", "in": "query", "description": "Required for private format=image preview. Must name a manifest-verified image referenced by output.md; uses job authorization, never a share grant.", "schema": { "type": "string", "maxLength": 512 } }],
        "responses": { "200": { "description": "Markdown, a verified customer delivery ZIP, or a private verified result image (format=image, at most 12 MiB; authorization required on every request)", "content": { "text/markdown": {}, "application/zip": {}, "image/*": {} } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/v1/jobs/{job_id}/preview": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": {
        "tags": ["Conversion"],
        "operationId": "previewResultMarkdown",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "description": "Returns result Markdown for Review without unlocking a downloadable attachment. Guest previews remain gated from Download until settlement.",
        "responses": { "200": { "description": "Review-only Markdown", "content": { "text/markdown": {} } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/v1/jobs/{job_id}/source": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": {
        "tags": ["Conversion"],
        "operationId": "readRetainedReviewSource",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "description": "Returns the retained original source for the source-aware Review viewer while the guest handoff remains resumable.",
        "responses": { "200": { "description": "Original retained source", "content": { "application/pdf": {}, "image/png": {}, "image/jpeg": {}, "application/octet-stream": {} } }, "404": { "$ref": "#/components/responses/Error" }, "410": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/v1/jobs/{job_id}/assets": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": {
        "tags": ["Conversion"],
        "operationId": "listJobAssets",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "responses": {
          "200": { "description": "Manifest-verified Bundle image assets", "content": { "application/json": { "schema": { "type": "object", "required": ["job_id", "assets"], "properties": { "job_id": { "type": "string" }, "assets": { "type": "array", "items": { "$ref": "#/components/schemas/BundleAsset" } } } } } } },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/jobs/{job_id}/asset-grants": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "post": {
        "tags": ["Conversion"],
        "operationId": "createAssetGrant",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetGrantInput" } } } },
        "responses": {
          "201": { "description": "Revocable short-lived asset URL", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AssetGrant" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "410": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/jobs/{job_id}/asset-grants/{grant_id}": {
      "parameters": [
        { "$ref": "#/components/parameters/JobId" },
        { "name": "grant_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^ag_[a-f0-9]{32}$" } }
      ],
      "delete": {
        "tags": ["Conversion"],
        "operationId": "revokeAssetGrant",
        "security": [{ "bearerAuth": [] }, { "jobToken": [] }],
        "responses": {
          "204": { "description": "Asset grant revoked" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/asset-grants/{token}": {
      "parameters": [
        { "name": "token", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^mk_asset_[a-f0-9]{64}$" } }
      ],
      "get": {
        "tags": ["Conversion"],
        "operationId": "readAssetGrant",
        "security": [],
        "description": "Bearer capability URL. It expires in at most 10 minutes, can be revoked, and must not be logged or forwarded.",
        "responses": {
          "200": { "description": "Private Bundle image", "content": { "image/png": {}, "image/jpeg": {}, "image/webp": {}, "image/tiff": {}, "image/bmp": {}, "image/svg+xml": {} } },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/jobs/{job_id}/quality-report": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": { "tags": ["Conversion"], "operationId": "getQualityReport", "security": [{ "bearerAuth": [] }, { "jobToken": [] }], "responses": { "200": { "description": "Versioned Markovo quality evidence", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QualityReport" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } }
    },
    "/v1/jobs/{job_id}/source-map": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": { "tags": ["Conversion"], "operationId": "getSourceMap", "description": "Returns a customer-safe source provenance projection for review. Internal providers, routes, paths, hashes, statistics, and arbitrary metadata are omitted.", "security": [{ "bearerAuth": [] }, { "jobToken": [] }], "responses": { "200": { "description": "Versioned customer-safe source provenance", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SourceMap" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } }
    },
    "/v1/jobs/{job_id}/delivery-check": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": { "tags": ["Conversion"], "operationId": "getDeliveryCheck", "security": [{ "bearerAuth": [] }, { "jobToken": [] }], "responses": { "200": { "description": "Versioned Bundle delivery verification", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeliveryCheck" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } }
    },
    "/v1/jobs/{job_id}/result-feedback": {
      "parameters": [{ "$ref": "#/components/parameters/JobId" }],
      "get": { "tags": ["Conversion"], "operationId": "getResultFeedback", "security": [{ "bearerAuth": [] }, { "jobToken": [] }], "responses": { "200": { "description": "Current actor rating", "content": { "application/json": { "schema": { "type": "object", "properties": { "feedback": { "anyOf": [{ "$ref": "#/components/schemas/ResultFeedback" }, { "type": "null" }] } } } } } }, "404": { "$ref": "#/components/responses/Error" } } },
      "put": { "tags": ["Conversion"], "operationId": "putResultFeedback", "security": [{ "bearerAuth": [] }, { "jobToken": [] }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResultFeedbackInput" } } } }, "responses": { "200": { "description": "Rating saved", "content": { "application/json": { "schema": { "type": "object" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } },
      "delete": { "tags": ["Conversion"], "operationId": "deleteResultFeedback", "security": [{ "bearerAuth": [] }, { "jobToken": [] }], "responses": { "204": { "description": "Rating removed" }, "404": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } }
    },
    "/v1/account/usage": {
      "get": { "tags": ["Account"], "operationId": "getUsage", "security": [{ "bearerAuth": [] }], "responses": { "200": { "description": "Integer and decimal Credit balance", "content": { "application/json": { "schema": { "type": "object" } } } }, "401": { "$ref": "#/components/responses/Error" } } }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "Markovo API key" },
      "oauthBearer": { "type": "http", "scheme": "bearer", "bearerFormat": "Markovo OAuth access token" },
      "cookieSession": { "type": "apiKey", "in": "cookie", "name": "better-auth.session_token" },
      "jobToken": { "type": "apiKey", "in": "header", "name": "x-markovo-job-token" },
      "guestPreviewToken": { "type": "apiKey", "in": "header", "name": "x-markovo-preview-token" }
    },
    "parameters": {
      "JobId": { "name": "job_id", "in": "path", "required": true, "schema": { "type": "string", "minLength": 1, "maxLength": 128 } },
      "GuestPreviewSessionId": { "name": "session_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^gps_[a-f0-9]{32}$" } }
    },
    "requestBodies": {
      "ConversionUpload": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "string", "format": "binary" }, "capability_id": { "type": "string" }, "mode": { "type": "string", "enum": ["fast", "accurate"], "default": "fast" }, "guest_preview": { "type": "boolean", "default": false, "description": "Browser-only anonymous preview staging. A guest session must subsequently select the ordered continuous prefix and Download remains locked until exactly-once account settlement." }, "layout_fidelity": { "type": "string", "enum": ["standard", "formula"], "default": "standard", "description": "Formula is a signed-in PDF enhancement. Detected formula regions render as LaTeX; compare every equation with the original PDF. Detected formula regions render as LaTeX through the standard conversion path with no additional Credit charge." }, "pages": { "type": "string", "pattern": "^[0-9]+(?:-[0-9]*)?(?:,[0-9]+(?:-[0-9]*)?)*$", "description": "PDF only: 1-based page ranges like \"1-3,5,8-\" (an open end runs to the last page), at most 100 comma-separated parts. Only the selected pages convert and the estimate prices only them. Non-PDF capabilities reject it with page_range_unsupported; malformed or out-of-range values return invalid_page_range." } } } } } },
      "ConversionUploadWithMaximum": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "array", "items": { "type": "string", "format": "binary" } }, "capability_id": { "type": "string" }, "mode": { "type": "string", "enum": ["fast", "accurate"], "default": "fast" }, "layout_fidelity": { "type": "string", "enum": ["standard", "formula"], "default": "standard", "description": "REST, CLI, and MCP default to standard. Set formula explicitly for the signed-in PDF Formula enhancement." }, "max_credit_units": { "type": "integer", "minimum": 1 }, "max_credits": { "oneOf": [{ "type": "number", "multipleOf": 0.001 }, { "type": "string", "pattern": "^[0-9]+(?:\\.[0-9]{1,3})?$" }] }, "client_request_id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$", "description": "Optional idempotency key for this submission. Resending the same key replays the jobs the first attempt created instead of queueing duplicates; released when the job is deleted." }, "job_token": { "type": "string", "pattern": "^mk_job_[a-f0-9]{64}$", "description": "Anonymous callers may supply their own job token so a retry can prove ownership via the x-markovo-job-token header or by resending the same form. Only its hash is stored." } } } } } }
    },
    "responses": {
      "Error": { "description": "Machine-readable safe error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
    },
    "schemas": {
      "Estimate": { "type": "object", "required": ["upload_id", "credit_scale", "credit_model_version", "rate_card_version", "estimated_credit_units"], "properties": { "upload_id": { "type": "string" }, "job_token": { "type": "string", "description": "Anonymous estimate ownership token. Keep it private; URL guest previews submit it with upload_id to POST /v1/guest-preview-sessions." }, "capability_id": { "type": "string" }, "filename": { "type": "string" }, "source_units_estimated": { "type": "integer", "minimum": 0 }, "source_kind": { "const": "youtube_audio" }, "duration_ms": { "type": "integer", "minimum": 0, "description": "Present for YouTube audio estimates; billing uses decoded duration." }, "expires_at": { "type": "string", "format": "date-time" }, "credit_scale": { "const": 1000 }, "credit_model_version": { "type": "string" }, "rate_card_version": { "type": "string" }, "estimated_credit_units": { "type": "integer", "minimum": 0 }, "estimated_credits": { "type": "number", "minimum": 0 }, "layout_fidelity": { "type": "string", "enum": ["standard", "formula"] }, "formula_detection": { "type": "object", "additionalProperties": false, "required": ["requested", "applied", "reason", "additional_credit_units"], "properties": { "requested": { "const": true }, "applied": { "const": false }, "reason": { "const": "no_formula_regions" }, "additional_credit_units": { "const": 0 } }, "description": "Present when formula enhancement was requested but local preflight found no formula region. The estimate automatically uses standard conversion and adds no formula charge." }, "formula_candidate_pages": { "type": "array", "maxItems": 8, "uniqueItems": true, "items": { "type": "integer", "minimum": 0 } }, "formula_regions": { "type": "array", "maxItems": 32, "items": { "type": "object", "additionalProperties": false, "required": ["region_id", "page_index", "bbox"], "properties": { "region_id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" }, "page_index": { "type": "integer", "minimum": 0 }, "bbox": { "type": "array", "minItems": 4, "maxItems": 4, "items": { "type": "number", "minimum": 0 } } } }, "description": "Regions detected during unpaid formula preflight; the estimate shows the region count and maximum price before conversion." }, "external_processing": { "type": "object", "additionalProperties": false, "properties": { "required": { "type": "boolean" }, "transfer_scope": { "type": "string" } }, "description": "When required, only the detected formula region is transferred to a third-party service; the scope is disclosed before conversion." }, "page_ranges": { "type": "string", "description": "Normalized page selection echoed for pdf-to-markdown when the request included pages, e.g. \"1-3,5,8-12\"." }, "pages_selected": { "type": "integer", "minimum": 1, "description": "Number of source pages the estimate prices when a page selection applies." }, "source_pages_total": { "type": "integer", "minimum": 1, "description": "Real document page count a page selection was validated against." }, "billing_metrics": { "type": "object", "additionalProperties": { "oneOf": [{ "type": "number" }, { "type": "array", "uniqueItems": true, "items": { "type": "integer", "minimum": 0 } }] } } } },
      "JobConfirmation": { "type": "object", "required": ["upload_id"], "properties": { "upload_id": { "type": "string" }, "max_credit_units": { "type": "integer", "minimum": 1 }, "max_credits": { "oneOf": [{ "type": "number", "multipleOf": 0.001 }, { "type": "string", "pattern": "^[0-9]+(?:\\.[0-9]{1,3})?$" }] }, "client_request_id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$", "description": "Optional idempotency key for this confirmation. Resending the same key replays the already-created job instead of queueing a duplicate; released when the job is deleted." }, "webhook_url": { "type": "string", "format": "uri", "maxLength": 2048, "description": "Optional HTTPS endpoint that receives a signed POST when the job reaches a terminal state (job.succeeded, job.failed, job.cancelled, job.timed_out). Must use port 443 or the default, no userinfo, and no IP-literal host; invalid values return invalid_webhook_url. Deliveries carry a Markovo-Signature HMAC header verifiable with the organization's whsec_ secret from Account → API settings (GET/POST /v1/account/webhook-secret)." } }, "anyOf": [{ "required": ["max_credit_units"] }, { "required": ["max_credits"] }] },
      "ConfirmedJob": { "type": "object", "required": ["job_id", "status", "capability_id", "estimated_credits", "credit_scale", "credit_model_version", "rate_card_version", "estimated_credit_units"], "properties": { "job_id": { "type": "string" }, "status": { "type": "string", "enum": ["queued", "running", "succeeded", "failed", "cancelled", "timed_out"] }, "capability_id": { "type": "string" }, "estimated_credits": { "type": "number", "minimum": 0 }, "credit_scale": { "const": 1000 }, "credit_model_version": { "type": "string" }, "rate_card_version": { "type": "string" }, "estimated_credit_units": { "type": "integer", "minimum": 0 }, "job_token": { "type": "string", "description": "Returned only for an anonymous guest job." } } },
      "JobResolution": { "type": "object", "additionalProperties": false, "required": ["state"], "properties": { "state": { "type": "string", "enum": ["none", "credit_release_in_progress", "contact_support", "resolved"] }, "charged": { "type": "boolean" }, "action": { "type": "string", "enum": ["wait", "email_support"] }, "support_reference": { "type": "string", "pattern": "^sup_[a-f0-9]{12}$" } } },
      "Job": { "type": "object", "required": ["id", "status", "credit_scale", "credit_model_version", "rate_card_version", "resolution"], "properties": { "id": { "type": "string" }, "status": { "type": "string", "enum": ["queued", "running", "succeeded", "failed", "cancelled", "timed_out"] }, "capability_id": { "type": "string" }, "credit_scale": { "const": 1000 }, "credit_model_version": { "type": "string" }, "rate_card_version": { "type": "string" }, "estimated_credit_units": { "type": "integer", "minimum": 0 }, "actual_credit_units": { "type": ["integer", "null"], "minimum": 0 }, "estimated_credits": { "type": "number", "minimum": 0 }, "actual_credits": { "type": ["number", "null"], "minimum": 0 }, "deliverable": { "type": ["integer", "boolean", "null"] }, "billing_metrics": { "type": ["object", "null"], "additionalProperties": { "type": "number" } }, "error": { "type": ["string", "null"] }, "error_code": { "type": ["string", "null"] }, "resolution": { "$ref": "#/components/schemas/JobResolution" }, "progress": { "type": "object", "description": "Present only while a PDF/image OCR-chain job is running. Count of completed page receipts; retries reuse idempotency keys so pages_done is monotonic.", "properties": { "pages_done": { "type": "integer", "minimum": 0 }, "pages_total": { "type": "integer", "minimum": 1 } } } } },
      "GuestPreviewSessionInput": { "type": "object", "additionalProperties": false, "required": ["estimates"], "properties": { "estimates": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "type": "object", "additionalProperties": false, "required": ["upload_id", "job_token"], "properties": { "upload_id": { "type": "string" }, "job_token": { "type": "string" } } } } } },
      "GuestPreviewItem": { "type": "object", "additionalProperties": false, "required": ["sequence", "status"], "properties": { "sequence": { "type": "integer", "minimum": 0 }, "job_id": { "type": ["string", "null"] }, "status": { "type": "string", "enum": ["queued", "dispatching", "dispatched", "running", "succeeded", "failed", "cancelled", "timed_out", "deferred_credit_limit", "deferred_after_limit"] } } },
      "GuestPreviewSession": { "type": "object", "required": ["session_id", "session_token", "prefix_count", "estimated_credit_units", "authentication_required", "items", "expires_at"], "properties": { "session_id": { "type": "string" }, "session_token": { "type": "string" }, "prefix_count": { "type": "integer", "minimum": 0 }, "estimated_credit_units": { "type": "integer", "minimum": 0, "maximum": 5000 }, "authentication_required": { "type": "boolean" }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/GuestPreviewItem" } }, "expires_at": { "type": "string", "format": "date-time" } } },
      "GuestPreviewRecoveryError": { "type": "object", "required": ["error"], "properties": { "error": { "type": "object", "required": ["code", "action", "limit_type", "recovery"], "properties": { "code": { "const": "quota_exceeded" }, "action": { "const": "sign_in_or_wait" }, "limit_type": { "const": "daily_conversions" }, "daily_limit": { "type": "integer", "minimum": 0 }, "daily_used": { "type": "integer", "minimum": 0 }, "reset_at": { "type": "string", "format": "date-time" }, "recovery": { "$ref": "#/components/schemas/GuestPreviewSession" } } } } },
      "GuestPreviewClaim": { "type": "object", "required": ["status", "actual_credit_units"], "properties": { "status": { "const": "settled" }, "actual_credit_units": { "type": "integer", "minimum": 0, "maximum": 5000 } } },
      "BundleAsset": { "type": "object", "required": ["path", "bytes", "sha256", "content_type"], "properties": { "path": { "type": "string", "pattern": "^assets/" }, "bytes": { "type": "integer", "minimum": 0 }, "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, "content_type": { "type": "string", "enum": ["image/png", "image/jpeg", "image/webp", "image/tiff", "image/bmp", "image/svg+xml"] } } },
      "QualityReport": {"type":"object","additionalProperties":false,"required":["schema_version","document_score","pages_low_confidence","review_required","page_scores","summary"],"properties":{"schema_version":{"const":"markovo.quality-report.v1"},"document_score":{"type":"number","minimum":0,"maximum":1},"pages_low_confidence":{"type":"array","items":{"type":"integer","minimum":1}},"review_required":{"type":"boolean"},"page_scores":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["page_index","score","confidence","review_required"],"properties":{"page_index":{"type":"integer","minimum":1},"score":{"type":"number","minimum":0,"maximum":1},"confidence":{"type":"number","minimum":0,"maximum":1},"review_required":{"type":"boolean"}}}},"summary":{"type":"object","additionalProperties":false,"required":["pages_total","pages_low_confidence","pages_with_errors","average_page_score"],"properties":{"pages_total":{"type":"integer","minimum":0},"pages_low_confidence":{"type":"integer","minimum":0},"pages_with_errors":{"type":"integer","minimum":0},"average_page_score":{"type":"number","minimum":0,"maximum":1}}}}},
      "SourceMap": {"type":"object","additionalProperties":false,"required":["schema_version","source_unit_kind","pages"],"properties":{"schema_version":{"const":"markovo.source-map.v1"},"source_unit_kind":{"type":"string","maxLength":40},"pages":{"type":"array","maxItems":5000,"items":{"type":"object","additionalProperties":false,"required":["page_index","blocks"],"properties":{"page_index":{"type":"integer","minimum":1},"width":{"type":"number","minimum":0},"height":{"type":"number","minimum":0},"blocks":{"type":"array","maxItems":2000,"items":{"type":"object","additionalProperties":false,"required":["id","type"],"properties":{"id":{"type":"string","maxLength":160},"type":{"type":"string","maxLength":80},"confidence":{"type":"number","minimum":0,"maximum":1},"text":{"type":"string","maxLength":20000},"html":{"type":"string","maxLength":20000},"caption":{"type":"string","maxLength":2000},"image_path":{"type":"string","maxLength":512}}}}}}}}},
      "DeliveryCheck": { "type": "object", "additionalProperties": true, "required": ["schema_version", "version", "status", "deliverable", "summary", "checks", "metrics", "policy", "next_actions"], "properties": { "schema_version": { "const": "markovo.delivery-check.v1" }, "version": { "type": "string" }, "status": { "type": "string", "enum": ["deliverable", "review_required", "blocked"] }, "deliverable": { "type": "boolean" }, "summary": { "type": "object" }, "checks": { "type": "array", "items": { "type": "object" } }, "metrics": { "type": "object" }, "policy": { "type": "object" }, "next_actions": { "type": "array", "items": { "type": "string" } } } },
      "AssetGrantInput": { "type": "object", "additionalProperties": false, "required": ["asset_path"], "properties": { "asset_path": { "type": "string", "pattern": "^assets/", "maxLength": 512 }, "expires_in_seconds": { "type": "integer", "minimum": 60, "maximum": 600, "default": 300 } } },
      "AssetGrant": { "type": "object", "required": ["grant_id", "job_id", "asset_path", "url", "expires_at", "expires_in_seconds"], "properties": { "grant_id": { "type": "string", "pattern": "^ag_[a-f0-9]{32}$" }, "job_id": { "type": "string" }, "asset_path": { "type": "string", "pattern": "^assets/" }, "url": { "type": "string", "format": "uri" }, "expires_at": { "type": "string", "format": "date-time" }, "expires_in_seconds": { "type": "integer", "minimum": 0, "maximum": 600 } } },
      "ResultFeedbackInput": { "type": "object", "required": ["rating"], "properties": { "rating": { "type": "string", "enum": ["up", "down"] }, "reason_code": { "type": "string", "enum": ["missing_content", "reading_order", "table_formula", "garbled_text", "layout_assets", "other"] }, "comment": { "type": "string", "maxLength": 1000 } }, "additionalProperties": false },
      "ResultFeedback": { "type": "object", "required": ["rating", "created_at", "updated_at"], "properties": { "rating": { "type": "string", "enum": ["up", "down"] }, "reason_code": { "type": ["string", "null"] }, "comment": { "type": ["string", "null"], "maxLength": 1000 }, "created_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" } } },
      "ErrorEnvelope": { "type": "object", "required": ["error"], "properties": { "error": { "type": "object", "required": ["code", "message", "request_id", "retryable", "docs_url"], "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "request_id": { "type": "string", "format": "uuid" }, "retryable": { "type": "boolean" }, "docs_url": { "type": "string", "format": "uri" } } } } }
    }
  }
}
