{"openapi":"3.1.0","info":{"title":"MVP Data Marketplace API","version":"0.1.0","description":"The Data Marketplace API: publish, discover and buy data products programmatically. The /agent/v1 gateway and every /products route accept an API key in the X-API-Key header (apiKeyAuth) - create one under Settings > API Keys - so an agent needs no browser session. Publishing a data product end to end with a key is: POST /products, POST /products/{id}/resources/presign, PUT the bytes, POST /products/{id}/resources/{rid}/finalize, GET /products/{id}/resources/status until the scan completes, PUT the dictionary and sampling, PATCH pricing and licence, then POST /products/{id}/publish."},"servers":[{"url":"https://app.datamarketplace.io/proxy-api","description":"The API is served under the app origin's /proxy-api prefix, which is stripped by the reverse proxy before it reaches the service."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"}}},"security":[{"bearerAuth":[]}],"tags":[{"name":"health","description":"Service health and readiness."},{"name":"datasets","description":"Dataset upload, listing, PII scan, and publish."},{"name":"products","description":"Data products: the sellable unit above datasets."},{"name":"search","description":"Keyword + semantic dataset search."},{"name":"discover","description":"AI-assisted dataset discovery."},{"name":"orders","description":"Purchases, order history, and downloads."},{"name":"api-keys","description":"Agent API key management."},{"name":"agent","description":"Non-interactive agent gateway (X-API-Key auth)."}],"paths":{"/":{"get":{"tags":["health"],"summary":"Service health check","security":[],"responses":{"200":{"description":"Service is up.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/datasets":{"post":{"tags":["datasets"],"summary":"Create a dataset (multipart upload or JSON)","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","additionalProperties":true}},"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"201":{"description":"Dataset created; returns { dataset, warnings }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"get":{"tags":["datasets"],"summary":"List datasets","responses":{"200":{"description":"Dataset list.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/datasets/{id}":{"get":{"tags":["datasets"],"summary":"Get a dataset by id","parameters":[{"name":"id","in":"path","required":true,"description":"Dataset id","schema":{"type":"string"}}],"responses":{"200":{"description":"Dataset.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"delete":{"tags":["datasets"],"summary":"Delete a dataset","parameters":[{"name":"id","in":"path","required":true,"description":"Dataset id","schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/datasets/{id}/pii":{"get":{"tags":["datasets"],"summary":"Get a dataset's PII scan results","parameters":[{"name":"id","in":"path","required":true,"description":"Dataset id","schema":{"type":"string"}}],"responses":{"200":{"description":"PII scan report.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/datasets/{id}/publish":{"patch":{"tags":["datasets"],"summary":"Publish a dataset","parameters":[{"name":"id","in":"path","required":true,"description":"Dataset id","schema":{"type":"string"}}],"responses":{"200":{"description":"Published.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Unresolved PII findings block publish.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products":{"post":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Create a draft data product","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"201":{"description":"Product created; returns { product }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the target organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Slug already taken.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"get":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List products (published only, or mine=true for own drafts)","parameters":[{"name":"mine","in":"query","required":false,"description":"Seller view: the caller's own organization, drafts included.","schema":{"type":"boolean"}},{"name":"limit","in":"query","required":false,"description":"Page size (1–100, default 20).","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"Id of the last product from the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"Product list: { products, total, nextCursor }. Each product is a browse CARD: the summary fields plus the seller (`organization`: id, name, slug), `resourceCount`, the fail-closed `licenseClass` (open | proprietary | unspecified) and `qualityScore` — the LOWEST of the product's files, null when none of them has been scored yet.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}":{"get":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Get a product by id (governed: no storage keys, no samples)","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"responses":{"200":{"description":"Product detail with organization, resources and the quality roll-up (`quality`: composite score plus the completeness / schema-validity / dictionary-coverage / freshness components; null for a product with no published version).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"patch":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Update product metadata, readme, licence, or price","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Product updated; returns { product }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error (`status`/`slug` are not patchable).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/publish":{"post":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Freeze the product's current resources into a new semver `ProductVersion` and set the product `published` (runs the listing-policy gates first; re-hashes each resource's stored object against its commitment as defense-in-depth)","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string","description":"Semver, e.g. 1.0.0."},"changelog":{"type":"string","nullable":true},"acknowledgedResourceIds":{"type":"array","items":{"type":"string"},"description":"Resource ids whose non-blocking gate warnings the caller has seen and wants to publish through anyway."}},"required":["version"]}}}},"responses":{"200":{"description":"Published; returns { product, version, warnings }. `warnings` lists any non-blocking gate the caller was not forced through (e.g. the advisory dictionary-coverage gate).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Invalid version string.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"One of: `version_exists` (already published), `version_not_increasing` (must exceed `currentVersion`), a listing-policy gate rejection (missing dictionary, failed scan, an unpriced product — `price_required`, where a NULL `priceUsd` blocks but `0` is a publishable free product — etc. — `{ error, rejections: [{ gate, reason }] }`), `resource_object_missing` or `commitment_mismatch` (the stored object is gone or no longer matches what was verified at upload — the publish-time re-hash), or `product_not_publishable` (wrong status).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/unpublish":{"post":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Take a published product back to draft so it can be edited and re-published (prior versions stay immutable and resolvable)","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"responses":{"200":{"description":"Unpublished; returns { product } in `draft`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Product is not published.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/versions":{"get":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"List a product's published versions and changelogs","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"responses":{"200":{"description":"Version history: { versions }. Each version carries a buyer-safe `files` projection of its frozen manifest — { resourceId, name, format, byteSize, rowCount, sha256 } — where `sha256` is the commitment hash the same bytes are delivered under after purchase. No storage key and no dataset id is ever serialized.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources":{"post":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Add a file to a draft product (multipart: binary `file` part + JSON-string `metadata` part: { name?, description?, format? })","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"201":{"description":"Resource added; returns { resource }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"No file part, unsupported file type, file too large, or invalid metadata.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Product is not a draft, or the name is already used.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/{rid}":{"delete":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Remove a resource (and its file) from a draft product","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}},{"name":"rid","in":"path","required":true,"description":"Resource id","schema":{"type":"string"}}],"responses":{"200":{"description":"Removed; returns { id, productId, deleted: true }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Product or resource not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Product is not a draft.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/presign":{"post":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Reserve a resource id and mint an upload ticket — a real presigned PUT URL under the `spaces` storage driver, or this API's own direct-write endpoint under `local`","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string"},"sizeBytes":{"type":"integer"},"checksumSha256":{"type":"string","description":"Lowercase hex SHA-256, validated at the boundary."},"name":{"type":"string"},"format":{"type":"string","nullable":true},"contentType":{"type":"string","nullable":true}},"required":["filename","sizeBytes","checksumSha256"]}}}},"responses":{"201":{"description":"{ upload: { resourceId, uploadToken, uploadUrl, method: 'PUT', strategy: 'presigned' | 'direct', expiresAt, headers } } — send `headers` verbatim with the PUT, no client-side branching on `strategy` required.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error, `unsupported_file_type`, or `file_too_large` (the declared size exceeds this extension's ceiling).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Product is not a draft, or the name is already used.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/upload":{"put":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Direct-write a resource's bytes at the ticket's key (local storage driver only). Body is streamed, never buffered.","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"{ uploaded: true, resourceId }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Missing `x-upload-token` header, no body, the token is not bound to this product, or `file_too_large` against the declared `Content-Length`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"`direct_upload_unavailable` (active driver is `spaces`, not `local`), the product is not a draft, or the resource was already finalized.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/{rid}/finalize":{"post":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Verify the uploaded object (existence, size, streamed SHA-256 checksum), seal it to a key the upload capability cannot address, and create the resource — same governed shape the multipart relay endpoint returns","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}},{"name":"rid","in":"path","required":true,"description":"Resource id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"uploadToken":{"type":"string"},"description":{"type":"string","nullable":true}},"required":["uploadToken"]}}}},"responses":{"201":{"description":"Resource created; returns { resource }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"`invalid_upload_token` (expired, malformed, or not bound to this product/resource), `upload_not_found`, `upload_size_mismatch`, or `upload_checksum_mismatch`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Product is not a draft, the resource is already finalized, or the resolved name is already used by another resource on this product.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/status":{"get":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Scan status of every resource on a product, in upload order","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}}],"responses":{"200":{"description":"{ resources: [{ resourceId, name, position, scanStatus, piiStatus, piiFlagged, piiReviewApproved, settled, scanReport }] }. `piiReviewApproved` is true once an administrator recorded the personal-data review for that file's dataset — the acknowledgement a publish body may carry is only meaningful for a resource where it is true.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/{rid}/dictionary":{"get":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Read a resource's data dictionary, merged with the inferred structural schema (undocumented columns flagged) plus a coverage metric and any dangling reference warnings","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}},{"name":"rid","in":"path","required":true,"description":"Resource id","schema":{"type":"string"}}],"responses":{"200":{"description":"{ resource, fields, merged, coverage, referenceWarnings }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Product or resource not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"put":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Replace the dictionary from form JSON ({ fields }), a Frictionless table-schema subset ({ format: 'yaml', source }) or a CSV-of-fields ({ format: 'csv', source })","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}},{"name":"rid","in":"path","required":true,"description":"Resource id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Replaced; returns the same shape as GET.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error (neither `fields` nor `format`+`source`).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Product or resource not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Product is not a draft.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"422":{"description":"{ error: 'invalid_dictionary', source, errors: [{ line, path, message }] } — malformed import, duplicate field name, or unknown type.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/{rid}/sampling":{"put":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Set a resource's sampling policy: { mode: 'none' } | { mode: 'first_rows', config: { rowCount } } | { mode: 'custom_file', config: { customSampleDatasetId } }","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}},{"name":"rid","in":"path","required":true,"description":"Resource id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"{ resource, policy, availability } — the stored policy plus what the sampling gate would decide for it right now (no rows).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error (unknown key, or a mistyped value).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Product or resource not found, or the caller is not a member of the owning organization (deliberately not 403: a sampling policy must not confirm a product's existence to a stranger).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"{ error: 'sample_not_scanned' } — the named custom sample has not finished its own scan.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"422":{"description":"Mode and config disagree: `sampling_config_not_allowed`, `sampling_row_count_required`, `sampling_row_count_out_of_range`, `sampling_sample_dataset_required`, or `sample_dataset_not_found`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/{rid}/sample":{"get":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Serve the sample rows the seller's sampling policy authorises for one resource (never synthetic, never the primary file under `custom_file`)","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}},{"name":"rid","in":"path","required":true,"description":"Resource id","schema":{"type":"string"}}],"responses":{"200":{"description":"{ mode, synthetic: false, rows, truncated, inspectedScope } — `truncated` is measured by over-reading one row past the grant, and `inspectedScope` is null when the file's PII scan recorded no coverage (unknown, NOT full-file).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"{ error: 'invalid_file_path' } for a stored value that is not a key, or { error: 'dataset_read_failed' } when the reader cannot parse the granted file. Both come from the shared reader (`lib/duckdb.ts`), not from the sampling gate.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"{ error: 'product_not_found' } when the product does not exist or is a draft/unlisted/removed one the caller may not see; { error: 'resource_not_found' } when the resource is not on this product; { error: 'dataset_file_unavailable' } when the granted file has gone missing from storage.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"{ error: 'sample_unavailable', reason } — the sampling gate refused, with `services/sampling.ts`'s verdict verbatim: `sampling_disabled`, `pii_not_cleared`, `scan_not_passed`, `sample_unavailable` (no usable curated file under `custom_file`), or `file_unavailable`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"429":{"description":"{ error: 'rate_limited' } — per-IP sliding window (SAMPLE_RATE_LIMIT_WINDOW_MS / SAMPLE_RATE_LIMIT_MAX); the response carries `Retry-After`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"502":{"description":"{ error: 'storage_unavailable' } — the object store failed to answer. Deliberately NOT flattened into a 404: a credential, network or throttling failure must not read as 'the file is gone' (`lib/storage.ts`).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/resources/{rid}/dictionary/prefill":{"post":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Draft dictionary rows from the resource's inferred structural profile (additive: existing rows are never touched)","parameters":[{"name":"id","in":"path","required":true,"description":"Product id","schema":{"type":"string"}},{"name":"rid","in":"path","required":true,"description":"Resource id","schema":{"type":"string"}}],"responses":{"201":{"description":"Drafted; returns the same shape as GET.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Caller is not a member of the owning organization.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Product or resource not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Product is not a draft, or the resource has no structural profile yet.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/search":{"get":{"tags":["search"],"summary":"Search datasets","parameters":[{"name":"q","in":"query","required":false,"description":"Search query string.","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"1-based page number.","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Paginated search results.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/discover":{"post":{"tags":["discover"],"summary":"AI-assisted dataset discovery","responses":{"200":{"description":"Discovery results.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/orders":{"post":{"tags":["orders"],"summary":"Create an order (purchase a product or a dataset)","description":"The body carries EXACTLY ONE of { productId } or { datasetId } — both keys, or neither, is a 400. { productId } buys the whole of the product's currentVersion, pinned at checkout so a later publish never changes what was bought; its files are served by GET /orders/{id}/downloads. { datasetId } is the original single-file shape.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"201":{"description":"Order created (pending when a payment is owed, completed when it is not); response is { order, checkoutUrl } where checkoutUrl is the hosted Stripe Checkout URL, or null when no payment is required (free, or already owned).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error, or the dataset is not purchasable.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"402":{"description":"Payment failed — the Checkout Session could not be opened.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Dataset not found, or product not found (also returned for a product the caller may not see).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"product_not_purchasable — the product is not published, has no current version, or carries no price.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"get":{"tags":["orders"],"summary":"List the current user's orders","responses":{"200":{"description":"Order list.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/orders/{id}":{"get":{"tags":["orders"],"summary":"Get an order by id","parameters":[{"name":"id","in":"path","required":true,"description":"Order id","schema":{"type":"string"}}],"responses":{"200":{"description":"Order.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/orders/{id}/download":{"get":{"tags":["orders"],"summary":"Get a signed download link for a purchased dataset","description":"DEPRECATED in favour of GET /orders/{id}/downloads, which delivers every file of the purchased product version. Still the only path for an order placed before version pinning existed; responses carry Deprecation and Link headers naming the successor. The link is api-relative and the route it names is authenticated, so it is fetched with the bearer token rather than navigated to, and `filename` is the name to save the bytes under.","deprecated":true,"parameters":[{"name":"id","in":"path","required":true,"description":"Order id","schema":{"type":"string"}}],"responses":{"200":{"description":"{ downloadUrl, filename, expiresAt }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Not the purchasing buyer.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"order_not_completed, or order_is_multi_file — a product-keyed order's goods are a manifest; use GET /orders/{id}/downloads.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/orders/{id}/file":{"get":{"tags":["orders"],"summary":"Stream the single purchased file behind a signed link","description":"DEPRECATED alongside GET /orders/{id}/download, which mints the links this route serves. Authenticated like every other order route — it re-runs the buyer entitlement ladder and treats the sig/expires pair as the link's expiry, not as its auth.","deprecated":true,"parameters":[{"name":"id","in":"path","required":true,"description":"Order id","schema":{"type":"string"}}],"responses":{"200":{"description":"The file's bytes, as an attachment.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Stored file path is not a readable object.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated, or the link is expired or tampered.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Not the purchasing buyer.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or the file is unavailable.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"order_not_completed, or order_is_multi_file — a product-keyed order's goods are a manifest; use GET /orders/{id}/downloads.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/orders/{id}/downloads":{"get":{"tags":["orders"],"summary":"Get presigned download links for every file of a purchase","description":"One short-lived presigned GET per entry of the manifest of the ProductVersion the order is PINNED to, so a later publish never changes what a past buyer receives. Storage keys are never serialized — the presigned URL is the only handle on an object.","parameters":[{"name":"id","in":"path","required":true,"description":"Order id","schema":{"type":"string"}}],"responses":{"200":{"description":"{ orderId, productId, version, expiresAt, files[] }.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Not the purchasing buyer.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Order not completed, or it has no pinned version to deliver.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/orders/{id}/files/{resourceId}":{"get":{"tags":["orders"],"summary":"Stream one purchased file (local storage driver only)","description":"Serves the bytes for a link returned by GET /orders/{id}/downloads when STORAGE_DRIVER is not `spaces` — the local driver mints file:// URLs no browser can fetch. Requires the same entitlement as the link minter PLUS the signed, expiring `token` query parameter that link carried.","parameters":[{"name":"id","in":"path","required":true,"description":"Order id","schema":{"type":"string"}},{"name":"resourceId","in":"path","required":true,"description":"Resource id within the purchased version","schema":{"type":"string"}}],"responses":{"200":{"description":"The file's bytes (Content-Disposition: attachment).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated, or an expired/invalid token.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Not the purchasing buyer.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"Order not completed, or it has no pinned version to deliver.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/users/api-keys":{"post":{"tags":["api-keys"],"summary":"Create an agent API key","responses":{"201":{"description":"API key created (raw key shown once).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"get":{"tags":["api-keys"],"summary":"List the current user's API keys","responses":{"200":{"description":"API key list.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/users/api-keys/{id}":{"delete":{"tags":["api-keys"],"summary":"Revoke an API key","parameters":[{"name":"id","in":"path","required":true,"description":"API key id","schema":{"type":"string"}}],"responses":{"200":{"description":"Revoked.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/agent/v1/discover":{"post":{"tags":["agent"],"summary":"Agent product discovery (BM25 over the published catalog)","description":"Ranked search over PUBLISHED data products. Published-only is enforced in the query builder, so no caller can widen it. Each hit is a `ProductSearchHit` — `{ id, slug, title, description, score, categories, licenseClass, qualityScore, priceUsd, resourceCount, hasPrimaryKeys, hasJoinKeys }` — and `results[].id` is a PRODUCT id: feed it to `GET /agent/v1/products/{id}`, whose resources carry the ids `/agent/v1/inspect/{id}` and `/agent/v1/query` accept.","security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","minLength":1,"description":"Natural-language search string."},"entity":{"type":"string","enum":["product","dataset"],"default":"product","description":"`dataset` selects the legacy MVP-018 dataset index, which does not exist on production (503 `search_index_missing`); omit it."},"filters":{"type":"object","description":"Optional narrowing. `format`/`status`/`maxPriceUsd` apply to `entity: \"dataset\"`; the rest are product filters and are ignored for datasets.","properties":{"format":{"type":"string"},"status":{"type":"string"},"maxPriceUsd":{"type":"number"},"category":{"type":"string"},"country":{"type":"string"},"licenseClass":{"type":"string","enum":["open","proprietary","unspecified"]},"updateFrequency":{"type":"string"},"hasPrimaryKeys":{"type":"boolean"},"hasJoinKeys":{"type":"boolean"},"minQualityScore":{"type":"number"}}},"limit":{"type":"integer","minimum":1}},"required":["query"]}}}},"responses":{"200":{"description":"`{ results, total, query }`. `results` is a `ProductSearchHit` array (a `SearchHit` array for `entity: \"dataset\"`).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"`search_unavailable` (Elasticsearch unreachable) | `search_index_missing` (the index does not exist).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/agent/v1/inspect/{id}":{"get":{"tags":["agent"],"summary":"Agent live dataset inspection (schema, quality, samples)","security":[{"apiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Dataset id, or the `DataResource` id a product spec gives.","schema":{"type":"string"}}],"responses":{"200":{"description":"Inspection result.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"`resource_rejected` (a moderator rejected or removed the file — terminal, do not retry) | `dataset_not_published` (the product this file belongs to is not published — or, for a legacy standalone dataset, the dataset itself is not).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"`dataset_not_found` | `dataset_file_unavailable`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/agent/v1/query":{"post":{"tags":["agent"],"summary":"Agent structured filter-DSL query against a dataset","security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"Query rows.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error or field not in schema.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"402":{"description":"`no_license` — no licence assigned for this dataset.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"`resource_rejected` (a moderator rejected or removed the file — terminal, do not retry) | `dataset_not_published` (the product this file belongs to is not published — or, for a legacy standalone dataset, the dataset itself is not) | `license_denied` (an assigned licence exists but its policy refuses this action).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"`dataset_not_found` | `dataset_file_unavailable`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"`human_approval_required` — the assigned licence carries a duty the evaluator cannot verify on its own; the request awaits human review.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/agent/v1/compute":{"post":{"tags":["agent"],"summary":"Agent privacy-preserving aggregate over a licensed dataset","description":"Runs one aggregation server-side and returns only the aggregate rows plus an execution proof — raw rows never leave the provider. Groups smaller than the licence's `minAggregationGroupSize` (default 10) are dropped before the response is built, and `kAnonymity.groupsDropped` reports how many. Unlike `/inspect`, this operation is behind the licence middleware, and unlike `/query` the assigned licence's policy must permit the `compute` action.","security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"datasetId":{"type":"string","minLength":1,"description":"Dataset id, or the `DataResource` id a product spec gives — the same id space `/inspect` and `/query` take."},"aggregation":{"type":"string","enum":["count","sum","avg","min","max","stddev","median"],"description":"Case-insensitive. Anything else is a 400 `unsupported_aggregation`."},"column":{"type":"string","minLength":1,"description":"Column the aggregation is applied to."},"groupBy":{"type":"string","description":"Optional grouping column. Groups below the minimum size are suppressed."},"filters":{"type":"array","description":"Optional row filter, applied before aggregation.","items":{"type":"object","properties":{"field":{"type":"string"},"op":{"type":"string"},"value":{"oneOf":[{"type":"string"},{"type":"number"}]}},"required":["field","op","value"]}}},"required":["datasetId","aggregation","column"]}}}},"responses":{"200":{"description":"`{ rows, rowCount, proof, kAnonymity }`. Aggregate rows only; groups below the licence's minimum k are suppressed.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error | `unsupported_aggregation` | an unknown column or invalid filter operator.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"402":{"description":"`no_license` — no assigned licence covers this dataset for the calling key. (The route also carries a defensive `license_required` fallback for a request that reaches the handler with no licence context; the licence middleware answers first, so `no_license` is what a caller sees.)","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"`resource_rejected` (a moderator rejected or removed the file — terminal, do not retry) | `dataset_not_published` (the product this file belongs to is not published — or, for a legacy standalone dataset, the dataset itself is not) | `license_denied` (an assigned licence exists but its policy refuses this action).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"`dataset_not_found` | `dataset_file_unavailable`.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"409":{"description":"`human_approval_required` — the assigned licence carries a duty the evaluator cannot verify on its own; the request awaits human review.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/products/{id}/spec":{"get":{"tags":["products"],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"summary":"Full structured product spec: metadata, licence class, per-resource dictionary with primary keys and join references, quality breakdown, sampling availability, current version","description":"The same document `GET /agent/v1/products/{id}` serves to agents. `id` accepts a product id, a product slug, or a dataset id (the /datasets transition alias). Carries no sample rows and no storage keys. Each resource's `sampling.inspectedScope` records what that file's personal-data scan actually inspected: `basis` \"rows\" (a bounded parsed sample) or \"file\" (a whole-file byte scan), with `rowsInspected` (null for a whole-file byte scan, which has no row concept) / `totalRows` (null when the reader never counted them) and `sampleBounded`, true when the scan saw strictly less than the whole source. It is null when the scan did not record a scope — treat that as unknown coverage, never as full.","parameters":[{"name":"id","in":"path","required":true,"description":"Product id, product slug, or dataset id","schema":{"type":"string"}}],"responses":{"200":{"description":"Product spec.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or a draft the caller may not see.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/discover/products":{"post":{"tags":["search"],"summary":"BM25 product discovery with structured filters","description":"Filters: category, country, licenseClass, updateFrequency, format, hasPrimaryKeys, hasJoinKeys, minQualityScore, maxPriceUsd. Published products only.","responses":{"200":{"description":"Ranked product hits.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Validation error.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Unauthenticated.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"`search_unavailable` (Elasticsearch unreachable) | `search_index_missing` (the index does not exist).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/agent/v1/products/{id}":{"get":{"tags":["agent"],"summary":"Agent product spec (MCP `describe_product`)","security":[{"apiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Product id, product slug, or dataset id","schema":{"type":"string"}}],"responses":{"200":{"description":"Product spec.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Not found, or not visible to the key's user.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}}}}