feat(openapi): describe the property-catalog fact-contribution endpoints#5787
Merged
Conversation
The catalog fact surface (POST /api/registry/resolve, /catalog/disputes) is live and serves real traffic, but catalog-api.ts registered zero OpenAPI paths — the whole router was absent from static/openapi/registry.yaml. Since SDK clients generate their types from the published OpenAPI (adcp-client's types.generated.ts sources https://agenticadvertising.org/openapi/registry.yaml), the "send us facts" surface could not be typed. This makes it part of the contract. - New server/src/schemas/catalog-openapi.ts (mirrors the member-agents-openapi.ts pattern — standalone so the generator imports it without route-factory deps). Registers: resolveIdentifiers (POST /api/registry/resolve), fileCatalogDispute (POST /api/registry/catalog/disputes), getCatalogDispute (GET .../disputes/{id}). - Schemas mirror the Zod validators + result types in catalog-api.ts / catalog-db.ts / catalog-governance.ts exactly: the provenance enum, the identifiers[1..10000] cap, ResolveResponse.summary's five fields (incl not_found), and the dispute TriageResult ({dispute_id, action_taken, reason}). - property_rid is documented as a non-authoritative join/match handle, never an authorization credential (mirrors the #5750 identity-not-authorization lesson). - Wired into scripts/generate-openapi.ts + a "Property Catalog" tag; regenerated static/openapi/registry.yaml. Prerequisite for the SDK fact-contribution surface (specs/sdk-fact-contribution.md, #5782): once this publishes, @adcp/client and adcp regenerate types and add the reportIdentifiers()/disputeFact() methods. Read-side (browse/sync) OpenAPI is a fast follow-on. No behavior change — documentation of live endpoints only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Contributor
There was a problem hiding this comment.
Accurate documentation of three already-live endpoints — the schemas mirror the runtime, which is the whole job of an OpenAPI-registration PR. Approving: this makes the fact-contribution surface typeable for SDK codegen without touching a single wire behavior.
Things I checked
- Request validators mirror the routes exactly.
ResolveRequest(identifiers.min(1).max(10000), the 8-value provenance enum,modedefaultresolve) matchescatalog-api.ts:58-70;CatalogDisputeRequest(claim10–2000,evidence≤5000 optional) matchescatalog-api.ts:72-78. - Response shapes mirror the result types.
ResolveResponse/ResolvedEntry— nullableproperty_rid/source,statusenumexisting|created|excluded, the five-fieldsummaryincludingnot_found— matchescatalog-db.ts:72-90and the return at:298-308.CatalogDisputeTriageResult'saction_takenenum matchescatalog-governance.ts:29-33. - Auth annotations match the handlers. Resolve documents
security+ a 401 and requiresreq.useronly inresolvemode (catalog-api.ts:169-171); disputes POST requires auth (catalog-api.ts:533); GET dispute is correctly annotation-free because the handler has no gate. - No changeset required.
static/openapi/**andscripts/generate-openapi.tsare deliberately outside the release/versioning path (.github/workflows/release.yml:65scopes it tostatic/schemas/source/,static/compliance/source/, and named build scripts). This is the registry REST surface, not the AdCP wire schemas —static/schemas/source/**is untouched. property_ridframing is load-bearing and consistent. Documented as a non-authoritative join/match handle, never an authorization credential, in both the field and operation descriptions — the #5750 identity-not-authorization lesson, correctly carried.
Follow-ups (non-blocking — file as issues)
- Public GET on
/catalog/disputes/{id}returns reporter PII. The handler doesSELECT *and returns the row verbatim (catalog-api.ts:559-570), soreported_by_emailships on an unauthenticated read.CatalogDisputeRecorddoesn't document that field, so.passthrough()hides the leak from the contract rather than fixing it. Pre-existing route behavior, not introduced here — so not a block on a docs PR — but this is the moment the endpoint becomes a published surface. Route should stop returning reporter email on the public path; keep it out of the documented schema either way. Worth its own issue. CatalogDisputeRecordunder-specifies the read. The row carries stable columns (resolution,resolved_by,resolved_at,updated_at;catalog-disputes-db.ts:8-23) that.passthrough()leaves untyped for SDK consumers. Pinning them (minus PII, per above) would give the disputes-read surface a real generated type.summary.resolveddouble-countscreated. It's computed asexisting + created(catalog-db.ts:296-306), so an SDK author summingresolved + created + excludedagainsttotalwill be off. One line on the field description prevents the misuse.
Minor nits (non-blocking)
statusexample'suspended'can never occur.DisputeRecordSchema.statususesexample: 'suspended', but a dispute status isopen|investigating|resolved|rejected|escalated(catalog-disputes-db.ts:26), andfileDisputesets it toinvestigating(catalog-governance.ts:60/71/82).link_suspendedis anaction_takenvalue on the triage result — the example crossed the two surfaces. Change it toinvestigating.
LGTM. Follow-ups noted below — the PII one deserves a separate fix, not a comment on this PR.
bokelley
added a commit
that referenced
this pull request
Jul 1, 2026
…5790) Completes the property-catalog OpenAPI surface started in #5787. The catalog read/consume endpoints are live but were absent from static/openapi/registry.yaml, so the SDK's read-side (browseCatalog/syncCatalog — the second half of the resolve→sync→build-lists loop from RFC #5782) couldn't be typed. - browseCatalog — GET /api/registry/catalog: filtered, cursor-paginated browse; each entry carries the property's identifiers. - syncCatalog — GET /api/registry/catalog/sync: delta since a `server_timestamp` watermark (capped 10,000/page), distinct from browse's opaque cursor. Schemas mirror the route handlers + catalog-db result types (browse entries carry identifiers[]; sync entries are raw CatalogProperty with timestamps). property_rid documented as a non-authoritative handle, consistent with #5787. Once published, adcp-client regenerates types and adds browseCatalog()/ syncCatalog() alongside the shipped resolveIdentifiers/fileCatalogDispute/ claim/verify surface (@adcp/sdk 9.7.0). No behavior change — documentation of live endpoints only. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The prerequisite that makes the SDK "send us facts" surface typeable — the "canonical registry spec" that adcontextprotocol/adcp-client#2317 explicitly defers to.
Problem
The catalog fact endpoints (
POST /api/registry/resolve,/catalog/disputes) are live and serve real traffic, butcatalog-api.tsregistered zero OpenAPI paths — the whole router was absent fromstatic/openapi/registry.yaml. Since SDK clients generate their types from the published OpenAPI (adcp-client'stypes.generated.tssourceshttps://agenticadvertising.org/openapi/registry.yaml), the primary fact-contribution surface (reportIdentifiers/disputeFactin #5782) couldn't be typed at all.Change (documentation of live endpoints — no behavior change)
server/src/schemas/catalog-openapi.ts(mirrors themember-agents-openapi.tspattern — standalone so the generator imports it without route-factory deps). Registers:resolveIdentifiers—POST /api/registry/resolve(the primary fact funnel)fileCatalogDispute—POST /api/registry/catalog/disputesgetCatalogDispute—GET /api/registry/catalog/disputes/{id}provenanceenum,identifiers[1..10000]cap,ResolveResponse.summary's five fields (inclnot_found), and the disputeTriageResult({dispute_id, action_taken, reason}).property_riddocumented as a non-authoritative join/match handle, never an authorization credential (the fix(registry): enforce identity-not-authorization on property-save (#5749 gap #2) #5750 identity-not-authorization lesson).scripts/generate-openapi.ts+ a "Property Catalog" tag; regeneratedregistry.yaml.Where it fits
savePropertyidentity facts — the identity contribute-back slice.reportIdentifiers()/disputeFact()on top — the primary "send us facts" funnel from RFC docs(specs): RFC for the SDK fact-contribution surface #5782, which this unblocks.Read-side OpenAPI (
browse/sync) is a fast follow-on. Validated:npm run build:openapiregenerates cleanly; typecheck clean; unit suite green.🤖 Generated with Claude Code