{"service":"ailang-parse","version":"0.9.0","base_url":"https://docparse.ailang.sunholo.com","protocol":"a2a/0.3","auth":{"schemes":[{"id":"api_key","type":"apiKey","in":"body","name":"apiKey","prefix":"dp_","description":"API key for data endpoints (parse, formats, unstructured)"},{"id":"firebase_bearer","type":"http","scheme":"bearer","issuer":"ailang-multivac-dev.firebaseapp.com","description":"Firebase ID token for device auth approval"}],"device_flow":{"request_url":"/api/v1/auth/device","poll_url":"/api/v1/auth/device/poll","approve_url":"https://www.sunholo.com/docparse/approve.html"}},"endpoints":[{"id":"parse","path":"/api/v1/parse","method":"POST","description":"Parse any document into structured content blocks. Accepts file upload (multipart/form-data), sample IDs, signed HTTPS URLs (sourceUrl), or GCS refs.","auth":{"required":true,"scheme":"api_key"},"quick_start":{"step_1":"Get an API key: POST /api/v1/auth/device","step_2_sample":"Parse a sample: curl -X POST $BASE/api/v1/parse -H 'Content-Type: application/json' -d '{\"sample_id\":\"sample_docx_basic\",\"outputFormat\":\"markdown\",\"apiKey\":\"dp_...\"}","step_3_upload":"Upload your own file: curl -X POST $BASE/api/v1/parse -F 'filepath=@your-file.docx' -F 'outputFormat=markdown' -F 'apiKey=dp_...'"},"file_upload":{"method":"multipart/form-data","field_name":"filepath","description":"Upload files directly using curl -F 'filepath=@file.docx'. The field name MUST be 'filepath'. All tiers can upload files within their size limits (Free: 10MB, Pro: 25MB, Business: 50MB).","example":"curl -X POST $BASE/api/v1/parse -F 'filepath=@report.docx' -F 'outputFormat=markdown' -F 'apiKey=dp_...'"},"input_schema":{"type":"object","content_types":["application/json","multipart/form-data"],"properties":{"filepath":{"type":"string","description":"File upload (multipart: -F 'filepath=@file.docx') or sample_id string (JSON). All tiers supported."},"sample_id":{"type":"string","description":"Built-in sample ID from GET /api/v1/samples (e.g. 'sample_docx_basic'). Works on all tiers."},"gcsRef":{"type":"string","description":"gs:// URI for pre-uploaded large files (Business tier only). Get upload URL via POST /api/v1/upload/url"},"sourceUrl":{"type":"string","description":"Public or signed https:// URL the server will fetch directly. Available on every tier; per-tier size cap (Free: 10MB, Pro: 25MB, Business: 50MB). Use this to skip the upload-via-your-backend step when files already live in cloud storage."},"outputFormat":{"type":"string","enum":["blocks","markdown","html","a2ui"],"default":"blocks"},"pdfBackend":{"type":"string","enum":["","pdftotext","docling","liteparse","ai"],"default":"","description":"PDF extraction backend. Empty string uses server default (pdftotext). 'pdftotext': fast deterministic text extraction via poppler — no AI cost. 'docling': IBM Docling layout analysis — no AI cost. 'liteparse': run-llama heading inference — no AI cost. 'ai': Gemini multimodal via Vertex AI — required for scanned/image-only PDFs. Explicit non-ai selection returns an error on failure rather than falling back to AI."}},"oneOf":[{"required":["filepath"]},{"required":["sample_id"]}]},"output_schema":{"type":"object","properties":{"result":{"type":"string","description":"JSON-encoded parse output"},"meta":{"$ref":"#/definitions/response_meta"}}},"error_codes":["INVALID_API_KEY","QUOTA_EXCEEDED","INPUT_NOT_FOUND","UNSUPPORTED_FORMAT","FORMAT_NOT_AVAILABLE","PARSE_FAILED","INVALID_ARGUMENT","TIER_UPGRADE_REQUIRED","INVALID_GCS_REF","FORBIDDEN","GCS_DOWNLOAD_FAILED","INVALID_SOURCE_URL","SOURCE_FETCH_FAILED","FILE_TOO_LARGE","WORKBOOK_TOO_COMPLEX"],"response_headers":["X-Request-Id","X-AilangParse-Format","X-AilangParse-Replayable","X-DocParse-Tier","X-DocParse-Quota-Remaining-Day","X-DocParse-Quota-Remaining-Month","X-DocParse-Quota-Remaining-Ai"],"error_envelope":{"error":{"code":"string — machine-readable error code","message":"string — human-readable description","retryable":"bool","suggested_fix":"string","details":"object — error-specific structured context"},"request_id":"string — top-level request_id, also returned as X-Request-Id header"},"cost":{"unit":"request","estimated_class":"small","quota_scope":"daily","ai_required_for":["pdf","png","jpg","gif","tiff","webp"]},"determinism":{"replayable":true,"sources_of_variance":["ai_model_version (PDF/image only)"]},"examples":[{"id":"parse_docx_blocks_basic","request":{"sample_id":"sample_docx_formatting","outputFormat":"blocks"},"expected":{"status":200,"response_shape":{"blocks":"array","metadata":"object"},"deterministic":true,"notes":"Office formats always return identical output for same input"}}]},{"id":"convert","path":"/api/v1/convert","method":"POST","description":"Convert a document to another format. Deterministic conversion only — parse to blocks, then generate. Accepts the same input modes as /api/v1/parse (multipart upload, sample IDs, signed HTTPS URLs, GCS refs) plus a target format.","auth":{"required":true,"scheme":"api_key"},"quick_start":{"step_1":"Get an API key: POST /api/v1/auth/device","step_2_sample":"Convert a sample: curl -X POST $BASE/api/v1/convert -H 'Content-Type: application/json' -d '{\"filepath\":\"sample_docx_basic\",\"target\":\"html\",\"apiKey\":\"dp_...\"}'","step_3_upload":"Convert your own file: curl -X POST $BASE/api/v1/convert -F 'filepath=@report.docx' -F 'target=pptx' -F 'apiKey=dp_...'","step_4_decode":"Binary targets return base64: read .encoding, then e.g. jq -r .content resp.json | base64 -d > out.pptx"},"file_upload":{"method":"multipart/form-data","field_name":"filepath","description":"Upload files directly using curl -F 'filepath=@file.docx'. The field name MUST be 'filepath'. Same per-tier size limits as /api/v1/parse (Free: 10MB, Pro: 25MB, Business: 50MB).","example":"curl -X POST $BASE/api/v1/convert -F 'filepath=@report.docx' -F 'target=odt' -F 'apiKey=dp_...'"},"input_schema":{"type":"object","content_types":["application/json","multipart/form-data"],"properties":{"filepath":{"type":"string","description":"File upload (multipart: -F 'filepath=@file.docx') or sample_id string (JSON). All tiers supported."},"target":{"type":"string","enum":["html","md","qmd","docx","pptx","xlsx","odt","odp","ods"],"description":"Output format. Case-insensitive; a leading dot is accepted, and 'markdown'/'htm'/'quarto' are aliases for md/html/qmd. Anything else returns UNSUPPORTED_TARGET_FORMAT, never a 500."},"gcsRef":{"type":"string","description":"gs:// URI for pre-uploaded large files (Business tier only). Get upload URL via POST /api/v1/upload/url"},"sourceUrl":{"type":"string","description":"Public or signed https:// URL the server will fetch directly. Available on every tier; per-tier size cap."},"pdfBackend":{"type":"string","enum":["","pdftotext","docling","liteparse","ai"],"default":"","description":"PDF extraction backend for the parse half of the conversion. Same semantics as /api/v1/parse."}},"required":["target"]},"output_schema":{"type":"object","description":"The generated document is returned inside JSON, not as a binary body. Branch on `encoding` to decode `content`.","properties":{"status":{"type":"string","const":"success"},"request_id":{"type":"string"},"source_format":{"type":"string","description":"Detected format family of the input document (e.g. 'zip-office')"},"source_subtype":{"type":"string","description":"Concrete input extension (e.g. 'docx')"},"target":{"type":"string","description":"Normalized target format"},"filename":{"type":"string","description":"Suggested filename: input stem + target extension"},"content_type":{"type":"string","description":"MIME type to save the decoded content as"},"encoding":{"type":"string","enum":["base64","utf8"],"description":"'base64' for the ZIP container targets (docx, pptx, xlsx, odt, odp, ods); 'utf8' for the text targets (html, md, qmd). Clients MUST branch on this rather than assume."},"size_bytes":{"type":"integer","description":"Decoded size of the generated document"},"content":{"type":"string","description":"The generated document, encoded per `encoding`"}}},"error_codes":["INVALID_API_KEY","QUOTA_EXCEEDED","UNSUPPORTED_TARGET_FORMAT","INPUT_NOT_FOUND","FORMAT_NOT_AVAILABLE","CONVERSION_FAILED","TIER_UPGRADE_REQUIRED","INVALID_GCS_REF","FORBIDDEN","GCS_DOWNLOAD_FAILED","INVALID_SOURCE_URL","SOURCE_FETCH_FAILED","FILE_TOO_LARGE","WORKBOOK_TOO_COMPLEX"],"response_headers":["X-Request-Id","X-AilangParse-Format","X-AilangParse-Target","X-AilangParse-Encoding","X-DocParse-Tier","X-DocParse-Quota-Remaining-Day","X-DocParse-Quota-Remaining-Month","X-DocParse-Quota-Remaining-Ai"],"cost":{"unit":"request","estimated_class":"small","quota_scope":"daily","description":"One request per conversion, on the same counters as /api/v1/parse. Output size does not affect the charge. The AI sub-quota is consumed only when the SOURCE format needs AI (pdf, images) — generation itself is pure compute.","ai_required_for":["pdf","png","jpg","gif","tiff","webp"]},"determinism":{"replayable":true,"sources_of_variance":["ai_model_version (PDF/image sources only)"],"notes":"Office-to-Office conversion is pure compute and byte-identical for identical input. AI generation from a prompt is a separate cost profile and is NOT available on this endpoint."},"examples":[{"id":"convert_docx_to_html","request":{"filepath":"sample_docx_formatting","target":"html"},"expected":{"status":200,"response_shape":{"encoding":"utf8","content":"string"},"deterministic":true}},{"id":"convert_docx_to_pptx","request":{"filepath":"sample_docx_formatting","target":"pptx"},"expected":{"status":200,"response_shape":{"encoding":"base64","content":"string"},"deterministic":true,"notes":"Decode content from base64 to get a valid .pptx"}}]},{"id":"formats","path":"/api/v1/formats","method":"GET","description":"List all supported input, output, and generation formats","auth":{"required":false},"cost":{"unit":"none","quota_scope":"none"},"determinism":{"replayable":true,"sources_of_variance":[]}},{"id":"health","path":"/api/v1/health","method":"GET","description":"Service health, version, uptime","auth":{"required":false},"cost":{"unit":"none","quota_scope":"none"},"determinism":{"replayable":true,"sources_of_variance":["uptime_ms"]}},{"id":"unstructured","path":"/general/v0/general","method":"POST","description":"Drop-in Unstructured.io API replacement","auth":{"required":true,"scheme":"api_key"},"input_schema":{"type":"object","properties":{"path":{"type":"string"},"strategy":{"type":"string","enum":["auto","hi_res","fast"],"default":"auto"}},"required":["path"]},"cost":{"unit":"request","quota_scope":"daily"},"determinism":{"replayable":true,"sources_of_variance":[]}},{"id":"samples","path":"/api/v1/samples","method":"GET","description":"List available sample documents for testing","auth":{"required":false},"cost":{"unit":"none","quota_scope":"none"},"determinism":{"replayable":true,"sources_of_variance":[]}},{"id":"estimate","path":"/api/v1/estimate","method":"POST","description":"Estimate cost and latency before parsing — no auth required","auth":{"required":false},"input_schema":{"type":"object","properties":{"filepath":{"type":"string"},"outputFormat":{"type":"string","enum":["blocks","markdown","html","a2ui"]}},"required":["filepath"]},"cost":{"unit":"none","quota_scope":"none"},"determinism":{"replayable":true,"sources_of_variance":[]}},{"id":"pricing","path":"/api/v1/pricing","method":"GET","description":"Machine-readable pricing tiers and credit costs","auth":{"required":false},"cost":{"unit":"none","quota_scope":"none"}},{"id":"keys_usage","path":"/api/v1/keys/usage","method":"POST","description":"Usage counters and quota limits for a key","auth":{"required":true,"scheme":"api_key"},"cost":{"unit":"none","quota_scope":"none"}},{"id":"device_auth","path":"/api/v1/auth/device","method":"POST","description":"Request device authorization code (RFC 8628)","auth":{"required":false},"input_schema":{"type":"object","properties":{"label":{"type":"string","description":"Agent label for identification"},"scope":{"type":"string","default":"parse"}}},"cost":{"unit":"none","quota_scope":"none"}},{"id":"device_poll","path":"/api/v1/auth/device/poll","method":"POST","description":"Poll for device authorization approval","auth":{"required":false},"input_schema":{"type":"object","properties":{"device_code":{"type":"string"}},"required":["device_code"]},"error_codes":["AUTHORIZATION_PENDING","DEVICE_CODE_EXPIRED"],"cost":{"unit":"none","quota_scope":"none"}},{"id":"device_inspect","path":"/api/v1/auth/device/inspect","method":"POST","description":"Inspect a pending device authorization request to see where it came from (IP, User-Agent, Referer, age) before approving. Used by the dashboard /approve.html trust panel. NO authentication — possession of the user_code is the proof.","auth":{"required":false},"input_schema":{"type":"object","properties":{"userCode":{"type":"string","description":"The user_code to inspect (e.g. ABCD-1234)"}},"required":["userCode"]},"output_schema":{"type":"object","properties":{"status":{"type":"string"},"user_code":{"type":"string"},"label":{"type":"string"},"scope":{"type":"string"},"created_at":{"type":"string"},"expires_at":{"type":"string"},"requesting_client":{"type":"object","properties":{"ip":{"type":"string"},"user_agent":{"type":"string"},"referer":{"type":"string"},"classification":{"type":"string"}}}}},"error_codes":["INVALID_ARGUMENT","INPUT_NOT_FOUND","INTERNAL_ERROR"],"cost":{"unit":"none","quota_scope":"none"}},{"id":"device_approve","path":"/api/v1/auth/device/approve","method":"POST","description":"Approve a pending device authorization. Called from the dashboard after the user signs in via Firebase. Requires Firebase ID token in Authorization: Bearer header.","auth":{"required":true,"scheme":"firebase_bearer"},"input_schema":{"type":"object","properties":{"userCode":{"type":"string","description":"The user_code shown by the device (e.g. ABCD-1234)"}},"required":["userCode"]},"error_codes":["INVALID_ARGUMENT","DEVICE_CODE_EXPIRED","AUTH_ERROR"],"cost":{"unit":"none","quota_scope":"none"}},{"id":"keys_list","path":"/api/v1/keys/list","method":"POST","description":"List API keys belonging to the authenticated user. Authenticates via API key in body — returns all keys owned by that key's user.","auth":{"required":true,"scheme":"api_key"},"input_schema":{"type":"object","properties":{"apiKey":{"type":"string","description":"Any active dp_ key owned by the user"}},"required":["apiKey"]},"error_codes":["INVALID_API_KEY"],"cost":{"unit":"none","quota_scope":"none"}},{"id":"keys_revoke","path":"/api/v1/keys/revoke","method":"POST","description":"Revoke an API key. Marks the key inactive in Firestore — subsequent requests with this key will fail.","auth":{"required":true,"scheme":"api_key"},"input_schema":{"type":"object","properties":{"keyId":{"type":"string","description":"The 12-char keyId to revoke"},"apiKey":{"type":"string","description":"Any active key owned by the same user (proves ownership)"}},"required":["keyId","apiKey"]},"error_codes":["INVALID_API_KEY","FORBIDDEN","INPUT_NOT_FOUND"],"cost":{"unit":"none","quota_scope":"none"}},{"id":"keys_rotate","path":"/api/v1/keys/rotate","method":"POST","description":"Rotate an API key: revoke the old one and mint a fresh key with the same label, tier, and userId. The new key is shown once.","auth":{"required":true,"scheme":"api_key"},"input_schema":{"type":"object","properties":{"keyId":{"type":"string","description":"The 12-char keyId to rotate"},"apiKey":{"type":"string","description":"The current key (must match keyId)"}},"required":["keyId","apiKey"]},"error_codes":["INVALID_API_KEY","FORBIDDEN"],"cost":{"unit":"none","quota_scope":"none"}},{"id":"upload_url","path":"/api/v1/upload/url","method":"POST","description":"Get a pre-authenticated GCS upload URL for large files exceeding the 32MB request limit (Business tier only). For files under 32MB, use direct multipart upload: curl -F 'filepath=@file.docx' to POST /api/v1/parse instead.","auth":{"required":true,"scheme":"api_key"},"input_schema":{"type":"object","properties":{"filename":{"type":"string","description":"Original filename"},"mimeType":{"type":"string","description":"MIME type (e.g. application/pdf, image/png)"}},"required":["filename","mimeType"]},"output_schema":{"type":"object","properties":{"upload_url":{"type":"string","description":"PUT file content to this URL"},"gcs_ref":{"type":"string","description":"Pass this to POST /api/v1/parse as gcsRef"},"method":{"type":"string","enum":["PUT"]},"content_type":{"type":"string"}}},"error_codes":["INVALID_API_KEY","TIER_UPGRADE_REQUIRED","SERVICE_UNAVAILABLE","GCS_UPLOAD_INIT_FAILED"],"cost":{"unit":"none","quota_scope":"none"},"tier_required":"business"},{"id":"requests_replay","path":"/api/v1/requests/replay","method":"POST","description":"Replay a previous parse request by request_id. Requires authentication — the authenticated user must own the original request.","auth":{"required":true,"note":"API key or Firebase JWT required; ownership verified"},"input_schema":{"type":"object","properties":{"request_id":{"type":"string","description":"The request_id from a previous parse response meta"}},"required":["request_id"]},"cost":{"unit":"none","quota_scope":"none"},"determinism":{"replayable":true,"sources_of_variance":[]}},{"id":"requests_history","path":"/api/v1/requests/history","method":"POST","description":"List parse history for a user. Returns recent parse requests with metadata.","auth":{"required":true,"scheme":"api_key"},"cost":{"unit":"none","quota_scope":"none"},"determinism":{"replayable":false,"sources_of_variance":["new_requests_since_last_call"]}}],"definitions":{"response_meta":{"type":"object","properties":{"request_id":{"type":"string","description":"Unique request identifier for replay/audit"},"quota_used":{"type":"integer"},"quota_remaining":{"type":"integer"},"replayable":{"type":"boolean"},"sample_id":{"type":"string","description":"If request used a sample, its ID"}}},"error_response":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["INVALID_API_KEY","QUOTA_EXCEEDED","INPUT_NOT_FOUND","UNSUPPORTED_FORMAT","PARSE_FAILED","INVALID_ARGUMENT","INTERNAL_ERROR","AI_UNAVAILABLE","AUTHORIZATION_PENDING","DEVICE_CODE_EXPIRED"]},"message":{"type":"string"},"retryable":{"type":"boolean"},"suggested_fix":{"type":"string"}},"required":["code","message","retryable"]}}}},"tool_definitions":{"mcp":"/mcp/","a2a":"/.well-known/agent.json","openapi":"/api/_meta/openapi.json"}}
