The route account requires authenticated membership. Nonmember and policy denials currently use 401 with
error; unauthenticated token/session requests use 401 with errors. These are not a standardized 403 contract. After authentication and account membership, the inherited trial-expired lock refuses all four writes with 402 before action-specific authorization, input validation or persistence. GET operations remain available to their authorized roles. The exact refusal body is {"error":"trial_expired_locked","message":"This workspace trial has ended. Restore billing from the billing page to continue."}; no marketplace records change and no cleanup job is queued. Global catalogue/registry data is shared code data; tenant settings, requests and connections are scoped through Current.account.
action_key retains dots in a single slash-free path segment, for example synthetic.read. Provider keys also use a slash-free segment. Action catalogue pages default to 1/25, cap page at 10000 and page size at 100; malformed values and wire query arrays (page[]=2&per_page[]=1) normalize to defaults. An out-of-range page returns an empty actions array and nullable next_page.
Declared and effective trust
The actions list reportspolicy_status: declared and each action’s key, name, label, nullable description, mutates, available_to_agent, and trust. The trust block contains trust_class, approval_tier, nullable approval_policy, reversible, nullable compliance_class, pre_action_narration, and emit_receipt. A declaration is not evidence of dispatch enforcement. The global actions list never reflects tenant overrides.
The manual action catalogue projects app_id, action_key, label, nullable description, mutates, input_schema, effective trust_class and approval_tier, and needs_reconnect. Retained expired/revoked/errored connections remain here with needs_reconnect: true; absent connections do not appear. User-scoped connections belong to the requesting member.
Generated definitions are read-only pairs of a standard mcp_tool (name, title, optional description, inputSchema, annotations) and the full internal trust block. Trust additionally carries code fields such as class, approval policy when declared, rate limit, idempotency key, action/app identity, kind, nullable provider key, connection scope, paths and schema version. The generator filters disabled/manual-only/non-auto actions and requires an active connection or enabled hook. It supplies no entitlement collaborator today. This endpoint is not an MCP transport, has no invoke route, and never executes tools.
Overlay boundaries today
No provider row resolves tosummary and org. GET settings returns only stored rows, not effective defaults. A flat provider PUT permits sync_depth (summary or full) and connection_scope (org or user). Choosing full widens data retention beyond summary; a persisted full-to-summary transition queues cleanup. Connection scope is stored but inactive and does not rescope existing connections.
A flat action PUT permits disabled, manual_only, nullable approval_tier and nullable positive integer rate_limit. Code approval tiers order none, notify, single_approver, dual_control; a supplied tier cannot fall below the code tier. A supplied rate cannot exceed the lower of the code rate and service default. The overlay registry clamps these two axes against code on reads. Disabled removes the definition; manual_only removes its agentic path. Action identity, schema, dispatch binding and other trust fields come from code.
These checks compare against code, not the preceding tenant value. Resetting disabled/manual_only to false, clearing rate_limit to null, lowering a previously tightened approval tier toward code, or deleting a stored override can widen the effective tenant policy. DELETE restores code defaults. Current tests prove these accepted resets and reject limits above code. This is not a monotonic tighten-only promise.
Body account_id and user_id do not select ownership. Unpermitted fields are ignored. Controllers permit flat fields: JSON wrapping of flat requests does not make nested-only settings/request envelopes a supported input. Nested-only settings writes leave existing fields/defaults unchanged; a nested-only integration request fails required app-name validation. Contract tests pin these behaviors.
Requests and errors
Request creation storesrequested_app, optional website/urgency/note, caller account and caller user. A valid catalogue key wins; otherwise app_name is squished, case-insensitively matched to catalogue names, or stored as typed. Unknown catalogue keys fall back to typed app_name. Urgency accepts nice_to_have, this_quarter, blocking; requested_app is required, maximum 100 characters, website 255, note 2000. The 201 body has id, requested_app and created_at. No integration is connected by submission.
Unknown integration_app_id returns an empty 200 actions list. Missing integration_app_id is 422. Unknown provider/action overlay writes are 422 with a single human-readable error sentence; invalid overlay enum/rate fields also return one error sentence. Request validation instead returns message and attributes containing invalid attribute names. A missing stored override on DELETE returns 404. There is no field-level error object. ENG-5874 and ENG-5831 track the missing consistent unknown-provider/validation behavior. This documentation makes no future validation guarantee.
Evidence
spec/contract/marketplace_spec.rb performs persisted Rails integration requests in an isolated database using authenticated synthetic administrators/members. The catalogue and registry are synthetic collaborators; controller responses, policies, database associations, serializers and generator are real. No real provider descriptions, credentials or customer data are used. Tool-list tests assert that neither dispatch service nor dispatcher nor network is called.
Capture records under docs/platform-api/marketplace-evidence contain method, route template, status, JSON body (null for empty 204), synthetic provenance and exact fixture/helper/controller and inherited trial-lock source hashes. The capture guard rejects non-JSON responses before any write. Headers, auth tokens and cookies are omitted. The importer checks provenance, every capture checksum and source freshness before importing examples. It validates all JSON bodies with OAS 3.0 semantics, including nulls. These are synthetic running Rails request captures, not production traffic or provider conformance evidence.