Looking for the full interactive reference?
Open API Reference →Review policy
Eleven automated checks run synchronously on submit; manual review follows when all blockers pass. Below: the full check catalog, the SLA disclaimer, the conflict-of-interest rule, rejection-notes contract, resubmission rule, and the public-listing requirement.
Automated checks (run on submit)
Eleven deterministic checks run synchronously when a builder submits a listing for review. block severity fails the submission; warn severity surfaces in the admin queue but does not stop the draft → pending_review transition.
| Check | Pass criteria | Severity |
|---|---|---|
| manifest_required_fields | name, description (≥120 chars), category, homepage_url, support_email, required_scopes[] non-empty. | block |
| scopes_known | Every entry in required_scopes is one of the 13 documented public.* scopes. | block |
| no_wildcard_scopes | No scope contains a wildcard (*) or a :write suffix. v2.1 is read-only. | block |
| scopes_justified | Each required scope has a justification of at least 40 characters. | block |
| webhook_url_https | If webhook_url_template is present, it uses the https:// scheme. | block |
| webhook_url_reachable | HEAD/OPTIONS to the webhook URL returns 2xx, 3xx, or 405 within 5 seconds. | warn |
| developer_app_active | The backing developer app is in status='active' at submit time. | block |
| developer_app_age_min | Backing developer app was created at least 48 hours before submit. Skipped for app_type='internal'. | warn |
| install_url_https | If install_url is present, it uses the https:// scheme. | block |
| category_known | Category is one of: accounting, maintenance, training, analytics, verification, other. | block |
| no_duplicate_slug | No other listing with the same slug is currently in status='public'. | block |
Check names are stable. New checks may be added in future versions; existing names will not be repurposed.
Manual review
After all block-severity automated checks pass, the listing enters the manual review queue. Reviewers look at clarity of the description, quality of scope justifications, webhook-receiver reachability under realistic load, and alignment with the category vocabulary. The goal is a small high-trust catalog, not a high-volume marketplace.
SLA disclaimer
Conflict-of-interest rule
A reviewer cannot approve a non-internal listing whose developer_app.owner_org_id matches their own organisation, or whose created_by_user_id matches their own user account. Internal first-party listings (app_type='internal') are exempt. The constraint is enforced at the API layer; reviewers who attempt the action see an explicit 403 rather than a silent no-op.
State machine
Listings move through: draft → pending_review (on submit, all blockers pass) → approved (reviewer accepts) or rejected (reviewer declines with notes). From approved, builders can transition to public (visible to consumers) or back to draft (pull from the catalog). Rejected listings can be edited and resubmitted; doing so clears the prior verdict and re-runs all 11 checks.
Rejection notes
Rejection notes are required and must be at least 40 characters. They are delivered to the builder via in-app notification and email, and persist on the listing record for audit. Rejection notes should be specific enough that a builder can act on them without follow-up correspondence.
Resubmission
Editing and resubmitting a rejected listing clears the prior review_status and re-runs all 11 automated checks from scratch. There is no fast-path for previously-passing checks — the catalog is small enough that a fresh run is the simpler contract.
Public-listing requirement
The database CHECK constraint enforces that status='public' is only permitted when review_status='approved'. Builders cannot publish a listing that has not cleared review, even via direct database access. A listing in any other state is invisible to consumer orgs.