Publishing a provider
Overview
A provider packages pipeline capabilities — a build step, a deploy step, a notifier — as container images plus a manifest saying what each one takes in, what it gives back, and how it is configured. Publishing puts yours in the provider catalog, where other people can install it into their group and wire it into their pipelines. You register once as a publisher, claim a namespace that prefixes everything you ship, and from then on submit one version at a time.
The tier you publish to decides who has to trust what. At the verified tier we review your submission, copy your image into our own registry, sign it and publish it, so consumers trust us rather than you. At the community and private tiers you publish without a review and your registry stays the home of the bytes. A community provider reaches an organisation only once that organisation opts in to the community tier; a private provider belongs to the single organisation you name when you submit, and nobody outside it ever sees that it exists.
Before you start
- A signed-in account whose email address is confirmed.Registering a publisher with an unconfirmed address is refused with
403. Reading publisher profiles needs nothing more than a signed-in account, but every submit, withdraw and version-lifecycle call is refused with403until the calling identity owns a publisher. - A namespace you are willing to keep.Three to thirty-two characters, lowercase letters, digits and hyphens, no hyphen at either end. It is claimed first-come, it is unique across the whole platform, it prefixes the id of every provider you ever ship, and it cannot be changed afterwards. The names an organisation cannot take are refused here too: words Gitmoap keeps, well-known organisations' names, and names that look like one — see How organisation names work.
- Container images already pushed to a registry, with an image reference per capability, each a fully-qualified name that includes the registry host —
ghcr.io/acme/deploy-provider:v1.0.0— together with itssha256:digest. A bare name such asdeploy:latestis refused, because a name with no registry host is ambiguous about where the bytes came from. The image has to be pullable without credentials: a submission that carries registry credentials is refused with400at every tier. - A manifest for the version.It states a contract version of
1, a provider block — the id, a name, a description, and the shape of the credentials a connection to the provider supplies — and between one and thirty-two capabilities. Each capability declares an id prefixed by the provider id (acme.deploy:push), the pipeline stage it belongs to, a name, a description written for whoever will use it, its input and output ports, its configuration schema, and a default timeout, which is 1800 seconds if you leave it out. - Token scopes, if you are calling the API or the CLI rather than clicking:
publisher:readto read publishers,publisher:manageto register, update and verify your own,provider_publishing:readto follow submissions,provider_publishing:writeto submit, withdraw and change a version's lifecycle. Scopes are fixed when a token is minted, so a release pipeline that only submits should carry onlyprovider_publishing:write. - A verified namespace, if you want the
verifiedtier.Publishing tocommunityorprivateneeds nothing more than an active publisher; theverifiedtier requires that your namespace has been proven first. A submission that names no tier asks forverified, so name the tier you want explicitly. - Maintainer on a root group, if you want the
privatetier.A private provider belongs to exactly one organisation, and you name that top-level group when you submit. Naming a subgroup, or naming a group at any other tier, is refused with400.
How it works
Three tiers, three trust stories
| Tier | Who may publish | Where the image lives at run time | Gate before it publishes | Who can install it |
|---|---|---|---|---|
verified | a publisher whose namespace is verified | our registry — we copy and sign your image | human review, then the copy-and-sign step | every organisation, by default |
community | any active publisher | your registry, unchanged | an automated check that the digest you attested really resolves | only organisations that have opted in to community providers |
private | any active publisher who is a Maintainer of the owning top-level group | your registry, unchanged | the same automated digest check | only the organisation you named, which accepts its own private providers unless it turns that off |
platform | nobody outside the platform team | our registry | — | everyone |
Asking for the platform tier in a submission is refused with 403. You cannot assign yourself the verified tier either: you ask for it, and passing review is what confers it. The two self-service tiers you do assign, because their trust signal is the consuming organisation's own opt-in rather than anything we assert.
A submission that leaves the tier out asks for verified. That is the strictest reading of an omission, and it is the usual cause of a first submission being refused with 403 for an unverified namespace — name community explicitly if that is what you meant.
A provider's tier is fixed at its first publication. A later version of the same provider must ask for the tier it already has; moving a community provider to verified means publishing it again under a new id.
Namespaces and provider ids
A provider id is {namespace}.{slug} — acme.deploy, acme.notify. An id with no dot belongs to the platform's own providers (git, docker, aws) and is refused for anyone else. Because your namespace is unique, provider-id uniqueness follows: nobody else can ship acme.anything, and you cannot ship under somebody else's prefix.
Verifying your namespace proves you control the domain or the code-hosting organisation it names. It earns two things: eligibility for the verified tier, and a badge on every provider you publish, which is what an organisation running a verified_only community policy looks at.
What a submission goes through
Nothing about publishing is synchronous. Submitting answers 202 with a submission id and a state of submitted; everything after that happens on its own and you follow it by reading the submission.
verified tier submitted ──► approved ──► mirroring ──► published │ │ │ └──► mirror_failed (final) ├──► changes_requested (final; fix it and submit again) └──► rejected (final) community and private tiers submitted ──► verifying ──► published └──► verification_failed (final; fix the digest, submit again) any tier, until approval or a final state submitted / verifying ──► withdrawn (final; you pulled it)Copying and signing both happen inside mirroring; there is no separate step you can see or influence.
changes_requested and rejected both carry the reviewer's reason, and neither can be edited in place: the fix is a fresh submission. A (provider, version) pair is held while a submission for it is still open, and is free again the moment that submission is rejected, has changes requested, is withdrawn, or fails — so the same version number is available for a corrected manifest.
What the platform sets, and what stays yours
At the verified tier the image reference you send is a source, not a destination. The published capability points at our copy of your image and carries the digest we recorded when we pushed it, and we sign that copy with our own key. At the community and private tiers the published reference is yours, unchanged, pinned to the digest you attested and confirmed once at publish time.
Either way, what runs is pinned to an exact digest. Pushing new bytes behind the same tag changes nothing for anyone already running your provider — and there is no way to change a published version, because a published (provider, version) pair is immutable. A correction is a new version.
The provider itself — its name, its description and the shape of the credentials a connection supplies — is taken from the first version that publishes. Later versions carry the same provider block, and anything you change in it is ignored. Only the capabilities, their images and the release notes move from version to version.
Release notes are the one piece of prose that rides along with a version: up to 64 KB of markdown, fixed once the version publishes. Everything else you write about the provider — the long description people read on its page, the categories and keywords they find it by, the worked examples — lives on the provider's marketplace listing, which you edit at any time without republishing anything. See Using providers.
How to
Register as a publisher
Registration is self-service and binds the publisher to the identity that calls it — your user account, or a service account if a release pipeline is going to do the publishing. One identity owns at most one publisher, so decide which identity should hold it before you call.
Pick the namespace carefully. It is the only field you cannot change later, and it is visible to everyone who ever browses one of your providers.
Open Marketplace, then Become a publisher. Fill in the namespace, a display name and a contact address, and choose Register. Registering is refused until your account's email address is confirmed. On success you land on your Publisher dashboard, showing your namespace, an unverified badge and an empty list of providers.
gitmoap provider-publishing register-publisher \ --contact-email <contact email> \ --display-name <display name> \ --namespace <namespace>curl -X POST https://api.gitmoap.example/publishers \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "contact_email": "<string>", "display_name": "<string>", "namespace": "<string>" }'A new publisher starts unverified and active. That is enough to publish to community and private straight away; the verified tier waits until you have proven the namespace. The response carries the publisher id every later call needs, so keep it — you can also find it again from the publisher list.
Prove you own your namespace
Verification is two calls with a manual step in between: ask for a challenge, place the token it gives you, then ask us to look for it. There are two methods, dns and github, and the target you name has to correspond to your namespace — a domain whose first label is your namespace, or a GitHub organisation whose login is your namespace. Anything else is refused with 400, which is what stops namespace acme being verified through notacme.com. A namespace that is a well-known organisation's name is held to more: only dns proves it, and only on a domain that organisation owns, so microsoft is proven at microsoft.com and never at a name somebody created on a shared DNS service, such as microsoft.mooo.com. A namespace Gitmoap keeps for itself is never proven, and the challenge answers 403.
Open your Publisher dashboard and choose Verify namespace. Pick DNS or GitHub, type the domain or organisation login, and choose Get token. The panel then shows the token, exactly where to place it, and when the challenge expires.
gitmoap provider-publishing request-namespace-challenge \ --publisher-id <id> \ --method manual|dns|github \ --target <target>curl -X POST https://api.gitmoap.example/publishers/{publisher_id}/verification-challenge \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "method": "<one of dns | github | manual>", "target": "<string>" }'Place the token where the instructions say. For dns, that is a TXT record at _gitmoap-challenge.<your domain> whose value is the token. For github, it is a verification.txt file at the root of a public <your org>/.gitmoap repository, on its main branch, containing the token. Then come back and finish.
Note
A challenge lives for 72 hours and there is only one pending at a time, so requesting a second challenge replaces the first and invalidates the token you were given. DNS records also take time to propagate; if the check misses, wait for your record's TTL and run the check again with the same token rather than requesting a new one.
Back on Verify namespace, choose Check now. On success the dashboard flips to a verified badge and records which method proved it. A miss leaves the challenge in place so you can fix the record and check again.
gitmoap provider-publishing verify-namespace-challenge --publisher-id <id>curl -X POST https://api.gitmoap.example/publishers/{publisher_id}/verification-challenge/verify \ -H "Authorization: Bearer $GITMOAP_TOKEN"Verifying is idempotent: checking an already-verified publisher answers 200 with its current state rather than failing, so a script that runs the check twice is safe.
Asking for manual as the method is refused with 400. A manual grant is a decision platform staff make on your behalf, and it is the route when neither the DNS nor the GitHub proof fits your namespace — write to us instead of calling for it.
Look up a publisher
Publisher profiles are public to anyone signed in, which is how a consumer weighing up a provider finds out who is behind it and whether the namespace is proven. The bound identity behind a publisher is not public — you see it on your own publisher, and platform staff see it everywhere.
Open Marketplace, then Publishers. The list pages through every publisher with its namespace, display name and verification badge. Switch the Verified only toggle to hide unverified namespaces.
gitmoap provider-publishing list-publisherscurl -X GET https://api.gitmoap.example/publishers \ -H "Authorization: Bearer $GITMOAP_TOKEN"Choose any publisher from the list, or follow the publisher link on a provider's page, to open its profile: namespace, display name, contact address, verification badge and the method that earned it.
gitmoap provider-publishing get-publisher --publisher-id <id>curl -X GET https://api.gitmoap.example/publishers/{publisher_id} \ -H "Authorization: Bearer $GITMOAP_TOKEN"Update your publisher profile
The display name, the contact address and the public key are yours to change at any time. The namespace, the account status and the verification badge are not: the first is permanent, and the other two are ours to set.
On your Publisher dashboard, choose Edit profile, change the display name, contact address or public key, and Save.
gitmoap provider-publishing update-publisher --publisher-id <id>curl -X PATCH https://api.gitmoap.example/publishers/{publisher_id} \ -H "Authorization: Bearer $GITMOAP_TOKEN"The public key is recorded on your profile and shown with it. It is not what signs a Verified image: we sign our copy of your image with our own key.
Keep the contact address current. It is where a reviewer's questions land, and it is what a consumer with a broken pipeline will use.
Submit a new provider
Use this for the very first version of a provider that is not in the catalog yet. It creates a submission, not a catalog entry — nothing about the provider is visible to anyone else until the submission reaches published.
The body carries the whole candidate version: a contract version of 1, the provider block, the version, every capability with its image reference and digest, the tier you are asking for, the owning group if the tier is private, and optional release notes. Between one and thirty-two capabilities, each with an id prefixed by the provider id, each with a digest of the exact form sha256: followed by sixty-four hex characters. The body accepts no fields beyond the ones the card lists — a misspelt field name is a 400, not a silently ignored value.
Open your Publisher dashboard and choose New provider. Paste or upload the manifest, choose the tier, add the image reference and digest for each capability, and choose Submit for publication. The app takes you straight to Submission tracking for the submission it just created.
gitmoap provider-publishing submit-provider \ --capabilities '<json>' \ --contract-version <n> \ --provider '<json>' \ --version <version>curl -X POST https://api.gitmoap.example/providers \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "capabilities[].capability_id": "<string>", "capabilities[].description": "<string>", "capabilities[].image_digest": "<string>", "capabilities[].image_ref": "<string>", "capabilities[].name": "<string>", "capabilities[].stage_type": "<one of build | custom | deploy | notify | publish | source | test>", "contract_version": "<int32>", "provider.auth_schema": "<object>", "provider.description": "<string>", "provider.id": "<string>", "provider.name": "<string>", "version": "<string>" }'Write the descriptions for the people who will use the provider, not for yourself. A capability's description is what somebody reads when they are deciding whether to pick it; a port's description is rendered beside that port in the pipeline editor, so it should say what flows through, what to connect it to and what happens if you leave it unwired — never a container path or an internal error code. A port description is optional and capped at 400 characters, but a port that ships without one leaves whoever wires it guessing.
Submit a new version of a provider you own
Once a provider exists, further versions go to its own submission endpoint. Ownership is checked against your publisher, the manifest's provider id has to match the one in the URL, and the tier has to match the tier the provider already has. A provider that has never published answers 404 here — its first version goes through the new-provider call instead.
Version numbers are strict: a leading v and three numeric parts, v1.0.0. No pre-release suffix, no build metadata, no ranges, no latest. A version that has already published is refused with 409, because a published version is immutable.
Open the provider from your Publisher dashboard, choose Versions, then New version. The provider block is fixed and read-only; fill in the new version number, the capabilities with their new image digests and the release notes, then Submit for publication.
gitmoap provider-publishing submit-provider-version \ --provider-id <id> \ --capabilities '<json>' \ --contract-version <n> \ --provider '<json>' \ --version <version>curl -X POST https://api.gitmoap.example/providers/{provider_id}/versions \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "capabilities[].capability_id": "<string>", "capabilities[].description": "<string>", "capabilities[].image_digest": "<string>", "capabilities[].image_ref": "<string>", "capabilities[].name": "<string>", "capabilities[].stage_type": "<one of build | custom | deploy | notify | publish | source | test>", "contract_version": "<int32>", "provider.auth_schema": "<object>", "provider.description": "<string>", "provider.id": "<string>", "provider.name": "<string>", "version": "<string>" }'Follow a submission to the end
A submission is the only place the outcome lives. Read it to see the current state, the reviewer's notes if a decision has been made, and — once it has published — the digests that were recorded.
Open Submission tracking from your Publisher dashboard, or follow the link the submit action gave you. The page shows a progress stepper for the state machine above, the reviewer's notes where there are any, and the resolved image digests once publication finishes.
gitmoap provider-publishing get --submission-id <id>curl -X GET https://api.gitmoap.example/provider-submissions/{submission_id} \ -H "Authorization: Bearer $GITMOAP_TOKEN"A failed self-service check writes its reason into the same notes field a reviewer would use, so verification_failed tells you what did not resolve without your having to guess.
Open a provider from your Publisher dashboard and choose Submissions to see every attempt at every version, newest first, each with its state and its version.
gitmoap provider-publishing list --provider-id <id>curl -X GET https://api.gitmoap.example/providers/{provider_id}/submissions \ -H "Authorization: Bearer $GITMOAP_TOKEN"Both reads are restricted to the owning publisher and platform staff, because a submission carries review notes that are nobody else's business. Once a submission reads published, the version is in the catalog and an organisation that accepts your tier can install it — see Using providers for what a consumer does next.
Withdraw a submission
Warning
Withdrawing is final. The submission stops where it is and can never be reopened, its review notes stay with it, and the only route forward is a brand-new submission of the same version. Nothing already published is affected — a withdrawal touches the submission, never the catalog.
Withdraw when you spot a mistake before a decision has been taken, or when you want to free a version slot so you can submit a corrected manifest under the same version number.
Open the submission in Submission tracking and choose Withdraw. The confirmation names the provider and version; withdrawal is immediate.
gitmoap provider-publishing withdraw --submission-id <id>curl -X DELETE https://api.gitmoap.example/provider-submissions/{submission_id} \ -H "Authorization: Bearer $GITMOAP_TOKEN"Withdrawal is only possible while the submission is still waiting on a decision or still being checked. Once it has been approved — which is when the copy-and-sign work starts — or once it has reached any final state, the call is refused with 409, because approved work holds platform resources a withdrawal cannot cleanly interrupt. A submission that already ended in changes_requested or rejected is final too: there is nothing left to withdraw, and its version slot is free already.
Deprecate a version
Deprecating is how you steer consumers off an old version without breaking them. A deprecated version still runs, and pipelines pinned to it keep working; what changes is that a pipeline referencing it gets a non-blocking warning when it is validated, and the version shows as deprecated wherever it is listed.
Open the provider from your Publisher dashboard, choose Versions, and use Deprecate on the version's row. The version list marks it deprecated straight away.
gitmoap provider-publishing deprecate-provider-version \ --provider-id <id> \ --version <id>curl -X POST https://api.gitmoap.example/providers/{provider_id}/versions/{version}/deprecate \ -H "Authorization: Bearer $GITMOAP_TOKEN"Deprecating does not remove the version from the list of versions a pipeline may pin, and it does not change which version is newest — a deprecated version can still be the latest one. Deprecating an already-deprecated version is a no-op. A version that has been sunset cannot be deprecated, and the request answers 409: sunset is permanent. If a version has to stop working altogether, that is a platform decision with a grace window attached, not a publisher one — ask us.
Bring a deprecated version back
Deprecation is reversible. Restoring returns the version to active, and the warning consumers were seeing stops.
On the same Versions list, use Restore on a deprecated version's row.
gitmoap provider-publishing undeprecate-provider-version \ --provider-id <id> \ --version <id>curl -X POST https://api.gitmoap.example/providers/{provider_id}/versions/{version}/undeprecate \ -H "Authorization: Bearer $GITMOAP_TOKEN"A version that has been sunset by the platform is a different matter and cannot be restored this way; the call answers 409. Deprecate and restore are the only two lifecycle moves a publisher has. Sunsetting is a platform action, taken with a grace window attached, and no call reverses it — the way past a sunset version is a replacement version.
Reference
13 endpoints
POST/publisherspublisher:manage
Register a publisher bound to the calling identity, claiming a namespace.
Token scope
publisher:manage
Counts against
publishers_per_owner
Send
Body
- contact_emailstring
- display_namestring
- namespacestring
- public_keyobjectoptional
Refuses
400401403409429Publisher limit reached for this owner
Receive
- contact_emailstring
- created_atstring
- display_namestring
- namespacestring
- ownerobjectoptional
- public_keyobjectoptional
- publisher_iduuid
- statusactivesuspendedAccount status of a
Publisher.Activepublishers may submit;Suspendedis a platform-admin hold. - updated_atstring
- verification_methodobjectoptional
- verification_targetobjectoptional
- verifiedboolean
- verified_atobjectoptional
GET/publishers/{publisher_id}publisher:read
Get a publisher's public profile.
Token scope
publisher:read
Send
Path
- publisher_iduuidPublisher id
Refuses
400Bad request — a path parameter is not a well-formed value of its type401403Forbidden — the caller's access level or token scope is insufficient404
Receive
The same shape register_publisher returns — 13 fields. Go to it
Show it here
- contact_emailstring
- created_atstring
- display_namestring
- namespacestring
- ownerobjectoptional
- public_keyobjectoptional
- publisher_iduuid
- statusactivesuspendedAccount status of a
Publisher.Activepublishers may submit;Suspendedis a platform-admin hold. - updated_atstring
- verification_methodobjectoptional
- verification_targetobjectoptional
- verifiedboolean
- verified_atobjectoptional
GET/publisherspublisher:read
List publishers, paginated.
Token scope
publisher:read
Send
Query
- verifiedbooleanoptionalFilter by verified flag
- limitint32optionalPage size (1-100, default 20)
- next_tokenstringoptionalOpaque pagination cursor
Refuses
401403Forbidden — the caller's access level or token scope is insufficient
Receive
items[]object13 fields
- contact_emailstring
- created_atstring
- display_namestring
- namespacestring
- ownerobjectoptional
- public_keyobjectoptional
- publisher_iduuid
- statusactivesuspendedAccount status of a
Publisher.Activepublishers may submit;Suspendedis a platform-admin hold. - updated_atstring
- verification_methodobjectoptional
- verification_targetobjectoptional
- verifiedboolean
- verified_atobjectoptional
- next_tokenobjectoptional
PATCH/publishers/{publisher_id}publisher:manage
Update a publisher's display name, contact email, or public key
Token scope
publisher:manage
Send
Path
- publisher_iduuidPublisher id
Body
- contact_emailobjectoptional
- display_nameobjectoptional
- public_keyobjectoptional
Refuses
400401403404409The request conflicts with the resource's current state; `reason` says how
Receive
The same shape register_publisher returns — 13 fields. Go to it
Show it here
- contact_emailstring
- created_atstring
- display_namestring
- namespacestring
- ownerobjectoptional
- public_keyobjectoptional
- publisher_iduuid
- statusactivesuspendedAccount status of a
Publisher.Activepublishers may submit;Suspendedis a platform-admin hold. - updated_atstring
- verification_methodobjectoptional
- verification_targetobjectoptional
- verifiedboolean
- verified_atobjectoptional
POST/publishers/{publisher_id}/verification-challengepublisher:manage
Start a namespace ownership challenge
request_namespace_challengePOST /publishers/{publisher_id}/verification-challengeOpen in the API referenceToken scope
publisher:manage
Send
Path
- publisher_iduuidPublisher id
Body
- methoddnsgithubmanualHow a publisher's
verifiedflag was earned.Manualis the platform-admin grant (D3);Dns/Githubare the self-service namespace-ownership proofs. - targetstringThe domain or GitHub organisation being verified. It must match the publisher's namespace.
Refuses
400401403404409The request conflicts with the resource's current state; `reason` says how
Receive
- expires_atstring
- instructionsstring
- methoddnsgithubmanualHow a publisher's
verifiedflag was earned.Manualis the platform-admin grant (D3);Dns/Githubare the self-service namespace-ownership proofs. - publisher_iduuid
- targetstring
- tokenstring
POST/publishers/{publisher_id}/verification-challenge/verifypublisher:manage
Complete a namespace ownership challenge
verify_namespace_challengePOST /publishers/{publisher_id}/verification-challenge/verifyOpen in the API referenceToken scope
publisher:manage
Send
Path
- publisher_iduuidPublisher id
Refuses
400Bad request — a path parameter is not a well-formed value of its type401403404409The request conflicts with the resource's current state; `reason` says how422502
Receive
The same shape register_publisher returns — 13 fields. Go to it
Show it here
- contact_emailstring
- created_atstring
- display_namestring
- namespacestring
- ownerobjectoptional
- public_keyobjectoptional
- publisher_iduuid
- statusactivesuspendedAccount status of a
Publisher.Activepublishers may submit;Suspendedis a platform-admin hold. - updated_atstring
- verification_methodobjectoptional
- verification_targetobjectoptional
- verifiedboolean
- verified_atobjectoptional
POST/providersprovider_publishing:write
Submit the first version of a new provider for review.
Token scope
provider_publishing:write
Counts against
provider_submissions_per_publisher
Send
Body
capabilities[]object10 fields
- cache_enabledbooleanoptional
- capability_idstring
- config_schemaobjectoptional
- default_timeout_secondsint32optional
- descriptionstring
- image_digeststring
- image_refstring
- namestring
- requires_dockerbooleanoptional
- stage_typebuildcustomdeploynotifypublishsourcetestThe pipeline stage a capability belongs to. Maps verbatim to the
stage_typefield of a capability ingitmoap-provider.yaml.
- changelogobjectoptional
- contract_versionint32
- owning_group_idobjectoptional
providerobject4 fields
- auth_schemaobject
- descriptionstring
- idstring
- namestring
- registry_pull_secretobjectoptional
- target_tiercommunityplatformprivateverifiedoptionalHow a pipeline provider is distributed and how much it is vouched for.
- versionstring
Refuses
400401Unauthenticated — no credential, or an invalid one403409429Open-submission limit reached for this publisher
Receive
- provider_idstring
- stateapprovedchanges_requestedin_reviewmirror_failedmirroringpublishedrejectedsigningsubmittedverification_failedverifyingwithdrawnWhere a provider submission stands, from review through to publication or refusal.
- submission_iduuid
- submitted_atstring
- versionstring
POST/providers/{provider_id}/versionsprovider_publishing:write
Submit a new version of an existing provider you own, for review.
Token scope
provider_publishing:write
Counts against
provider_submissions_per_publisher
Send
Path
- provider_idstringProvider id
Body
capabilities[]object10 fields
- cache_enabledbooleanoptional
- capability_idstring
- config_schemaobjectoptional
- default_timeout_secondsint32optional
- descriptionstring
- image_digeststring
- image_refstring
- namestring
- requires_dockerbooleanoptional
- stage_typebuildcustomdeploynotifypublishsourcetestThe pipeline stage a capability belongs to. Maps verbatim to the
stage_typefield of a capability ingitmoap-provider.yaml.
- changelogobjectoptional
- contract_versionint32
- owning_group_idobjectoptional
providerobject4 fields
- auth_schemaobject
- descriptionstring
- idstring
- namestring
- registry_pull_secretobjectoptional
- target_tiercommunityplatformprivateverifiedoptionalHow a pipeline provider is distributed and how much it is vouched for.
- versionstring
Refuses
400401Unauthenticated — no credential, or an invalid one403404409429Open-submission limit reached for this publisher
Receive
The same shape submit_provider returns — 5 fields. Go to it
Show it here
- provider_idstring
- stateapprovedchanges_requestedin_reviewmirror_failedmirroringpublishedrejectedsigningsubmittedverification_failedverifyingwithdrawnWhere a provider submission stands, from review through to publication or refusal.
- submission_iduuid
- submitted_atstring
- versionstring
GET/providers/{provider_id}/submissionsprovider_publishing:read
List a provider's submissions
Token scope
provider_publishing:read
Send
Path
- provider_idstringProvider id
Refuses
400Bad request — a path parameter is not a well-formed value of its type401403404Not found — or found and private, which is masked as absent
Receive
items[]object6 fields
- provider_idstring
- publisher_iduuid
- stateapprovedchanges_requestedin_reviewmirror_failedmirroringpublishedrejectedsigningsubmittedverification_failedverifyingwithdrawnWhere a provider submission stands, from review through to publication or refusal.
- submission_iduuid
- submitted_atstring
- versionstring
GET/provider-submissions/{submission_id}provider_publishing:read
Get a provider submission
Token scope
provider_publishing:read
Send
Path
- submission_iduuidSubmission id
Refuses
400Bad request — a path parameter is not a well-formed value of its type401403404
Receive
- attest_resultobjectoptional
- auth_schemaobject
manifestobject22 fields
capabilities[]object11 fields
- cache_enabledboolean
- capability_idstring
- config_schemaobjectThe JSON Schema a step's
configmust satisfy to use this capability. - default_timeout_secondsint32
- descriptionstring
- image_digestobjectoptionalThe exact build of the capability's container image, when the catalog records one. Pinned so a run cannot pick up a different build behind the same tag.
- image_uristringThe container image that runs the capability, with an immutable version tag.
- namestring
- requires_dockerboolean
- source_image_refobjectoptionalFor a verified provider, the image's location in its publisher's registry before gitmoap copied it. Absent for providers gitmoap builds itself.
- stage_typebuildcustomdeploynotifypublishsourcetestThe pipeline stage a capability belongs to. Maps verbatim to the
stage_typefield of a capability ingitmoap-provider.yaml.
- changelogobjectoptional
- contract_versionint32
- cosign_signature_refobjectoptionalWhere the cosign signature is stored in the registry, when the images are signed.
- lifecycle_stateactivedeprecatedsunsetoptionalLifecycle state of a published
ProviderVersionManifest.Activeis the default;Deprecatedstill runs but the trigger-time validator surfaces a warning;Sunsetruns only inside its grace window, after which a trigger referencing it fails validation. - manifest_digeststringThe SHA-256 digest of the version's
gitmoap-provider.yaml. May be empty. - provider_idstring
- published_atstring
- published_bystring
"platform"for platform-authored providers; a publisher id otherwise. - signedbooleanoptionalWhether the version's images are signed with gitmoap's cosign key, as verified and platform providers' are.
- sunset_atobjectoptionalWhen the version was sunset. Set only for a sunset version.
- sunset_enforced_atobjectoptionalWhen the sunset's grace period ends. After it, a run that uses the version fails to start.
DELETE/provider-submissions/{submission_id}provider_publishing:write
Withdraw a provider submission
Token scope
provider_publishing:write
Send
Path
- submission_iduuidSubmission id
Refuses
400Bad request — a path parameter is not a well-formed value of its type401403404409
Receive
No body.
POST/providers/{provider_id}/versions/{version}/deprecateprovider_publishing:write
Deprecate a published version
deprecate_provider_versionPOST /providers/{provider_id}/versions/{version}/deprecateOpen in the API referenceToken scope
provider_publishing:write
Send
Path
- provider_idstringProvider id
- versionstringVersion
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403404409
Receive
capabilities[]object11 fields
- cache_enabledboolean
- capability_idstring
- config_schemaobjectThe JSON Schema a step's
configmust satisfy to use this capability. - default_timeout_secondsint32
- descriptionstring
- image_digestobjectoptionalThe exact build of the capability's container image, when the catalog records one. Pinned so a run cannot pick up a different build behind the same tag.
- image_uristringThe container image that runs the capability, with an immutable version tag.
- namestring
- requires_dockerboolean
- source_image_refobjectoptionalFor a verified provider, the image's location in its publisher's registry before gitmoap copied it. Absent for providers gitmoap builds itself.
- stage_typebuildcustomdeploynotifypublishsourcetestThe pipeline stage a capability belongs to. Maps verbatim to the
stage_typefield of a capability ingitmoap-provider.yaml.
- changelogobjectoptional
- contract_versionint32
- cosign_signature_refobjectoptionalWhere the cosign signature is stored in the registry, when the images are signed.
- lifecycle_stateactivedeprecatedsunsetoptionalLifecycle state of a published
ProviderVersionManifest.Activeis the default;Deprecatedstill runs but the trigger-time validator surfaces a warning;Sunsetruns only inside its grace window, after which a trigger referencing it fails validation. - manifest_digeststringThe SHA-256 digest of the version's
gitmoap-provider.yaml. May be empty. - provider_idstring
- published_atstring
- published_bystring
"platform"for platform-authored providers; a publisher id otherwise. - signedbooleanoptionalWhether the version's images are signed with gitmoap's cosign key, as verified and platform providers' are.
- sunset_atobjectoptionalWhen the version was sunset. Set only for a sunset version.
- sunset_enforced_atobjectoptionalWhen the sunset's grace period ends. After it, a run that uses the version fails to start.
- versionstring
POST/providers/{provider_id}/versions/{version}/undeprecateprovider_publishing:write
Restore a deprecated version to active
undeprecate_provider_versionPOST /providers/{provider_id}/versions/{version}/undeprecateOpen in the API referenceToken scope
provider_publishing:write
Send
Path
- provider_idstringProvider id
- versionstringVersion
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403404409
Receive
The same shape deprecate_provider_version returns — 13 fields. Go to it
Show it here
capabilities[]object11 fields
- cache_enabledboolean
- capability_idstring
- config_schemaobjectThe JSON Schema a step's
configmust satisfy to use this capability. - default_timeout_secondsint32
- descriptionstring
- image_digestobjectoptionalThe exact build of the capability's container image, when the catalog records one. Pinned so a run cannot pick up a different build behind the same tag.
- image_uristringThe container image that runs the capability, with an immutable version tag.
- namestring
- requires_dockerboolean
- source_image_refobjectoptionalFor a verified provider, the image's location in its publisher's registry before gitmoap copied it. Absent for providers gitmoap builds itself.
- stage_typebuildcustomdeploynotifypublishsourcetestThe pipeline stage a capability belongs to. Maps verbatim to the
stage_typefield of a capability ingitmoap-provider.yaml.
- changelogobjectoptional
- contract_versionint32
- cosign_signature_refobjectoptionalWhere the cosign signature is stored in the registry, when the images are signed.
- lifecycle_stateactivedeprecatedsunsetoptionalLifecycle state of a published
ProviderVersionManifest.Activeis the default;Deprecatedstill runs but the trigger-time validator surfaces a warning;Sunsetruns only inside its grace window, after which a trigger referencing it fails validation. - manifest_digeststringThe SHA-256 digest of the version's
gitmoap-provider.yaml. May be empty. - provider_idstring
- published_atstring
- published_bystring
"platform"for platform-authored providers; a publisher id otherwise. - signedbooleanoptionalWhether the version's images are signed with gitmoap's cosign key, as verified and platform providers' are.
- sunset_atobjectoptionalWhen the version was sunset. Set only for a sunset version.
- sunset_enforced_atobjectoptionalWhen the sunset's grace period ends. After it, a run that uses the version fails to start.
- versionstring
Limits
One publisher per identity. A second registration from the same account or service account is refused with 409 and nothing is created. If you need a separate namespace, register it from a separate identity — a service account is the usual answer for a namespace that a team, rather than a person, should own.
Twenty open submissions per publisher. A submission occupies a slot from the moment you submit until it settles — published, rejected, changes requested, withdrawn, or failed. An approved one still occupies its slot right through the copy-and-sign step, and so does one the automated digest check is still running against. At twenty, a further submit is refused with 429 TOO_MANY_REQUESTS and no submission is created. Free a slot by withdrawing something you no longer want, or wait for a pending decision.
Thirty-two capabilities per submission, and at least one. A provider bigger than that has to split into two providers under the same namespace.
Field sizes the API checks. When you register or update a publisher: a namespace of 3 to 32 characters and a display name of 1 to 80. When you submit: an image reference of at most 512 characters, a port description of at most 400, and release notes of at most 64 KB. Each of these is refused with 400 and the message names the field.
None of these ceilings move with a plan. They are platform limits on the publishing surface rather than namespace quotas, so they are not on your group's usage page and a plan upgrade does not raise them. If one of them is genuinely in your way, get in touch through the contact address on your publisher profile.
Troubleshooting
403 FORBIDDENon register, mentioning a verified email
- Cause
- The address on your account has not been confirmed
- What to do
- Confirm the address from your account settings, then register again. Nothing was created by the failed attempt
403 RESERVED_NAMESPACEon register
- Cause
- The namespace is a word Gitmoap keeps, a platform provider id, or a well-known organisation's name
- What to do
- Choose a different namespace. Registering proves no domain, so there is no way to register a reserved name
403 NAME_RESEMBLES_RESERVEDon register
- Cause
- The namespace looks like a well-known organisation's name — a digit for a letter, a hyphen inside it, or a word such as
officialorsupportadded - What to do
- Choose a different namespace
409 CONFLICTon register, on the namespace
- Cause
- Somebody else claimed it first
- What to do
- Pick another namespace. Claims are first-come and are not transferred
409 CONFLICTon register, on the identity
- Cause
- This account or service account already owns a publisher
- What to do
- Use the publisher you have, or register from a different identity
400 BAD_REQUESTon register or update, naming a field
- Cause
- The namespace, the contact address or the display name fails its format rule
- What to do
- Fix the field named in the message. A namespace is 3 to 32 lowercase letters, digits and hyphens with no hyphen at either end; a display name is 1 to 80 characters
400 BAD_REQUESTon the challenge, about the target
- Cause
- The domain or organisation you named does not correspond to your namespace — or your namespace is a well-known organisation's name and the target is not a domain that organisation owns
- What to do
- Use a domain whose first label equals your namespace, or an organisation whose login equals it. For a well-known organisation's name, use
dnsand the domain the message names. Verifying a namespace through an unrelated target is exactly what this refusal prevents
403 RESERVED_NAMESPACEon the challenge
- Cause
- The namespace is a word Gitmoap keeps, which no proof releases
- What to do
- It cannot be verified. Publish to
communityorprivateunder it, or contact support@gitmoap.com
400 BAD_REQUESTon the challenge, about the method
- Cause
- The request asked for
manual - What to do
- Ask for
dnsorgithub. A manual grant is not something you can request through the API — write to us from the contact address on your publisher profile
404 NOT_FOUNDon the verify step
- Cause
- No challenge was ever requested, or the one you had expired after 72 hours
- What to do
- Request a fresh challenge and place the new token — an expired token is never accepted, even if it is still where you put it
422 UNPROCESSABLE_ENTITYon the verify step
- Cause
- We reached the target and the token was not there, or did not match
- What to do
- Check the record or file, wait out your DNS TTL if you only just published it, then run the check again with the same token
502 BAD_GATEWAYon the verify step
- Cause
- The lookup itself failed — the resolver or the code host did not answer
- What to do
- Retry. This is a transient failure of the lookup, not a verdict on your token, so nothing needs re-placing
403 FORBIDDENon submit, saying you are not a publisher
- Cause
- The calling identity owns no publisher, or its publisher is suspended
- What to do
- Register a publisher from this identity. If a release pipeline is publishing, the service account it runs as needs its own publisher, not yours
403 FORBIDDENon submit, about the namespace
- Cause
- The provider id is outside your namespace, or has no dot at all
- What to do
- Prefix the id with your namespace. A dotless id belongs to the platform
403 FORBIDDENon submit, about verification
- Cause
- You asked for the
verifiedtier with an unverified namespace — including by leaving the tier out, which asks forverified - What to do
- Name
communityexplicitly if that is the tier you wanted, or verify the namespace first and resubmit
403 FORBIDDENon submit, about the platform tier
- Cause
- The request asked for
platform - What to do
- Choose
verified,communityorprivate
400 BAD_REQUESTon submit, about a capability
- Cause
- A capability id is not prefixed by the provider id, a digest is not
sha256:plus 64 hex characters, or an image reference has no registry host - What to do
- Fix the capability the message names. Push the image and read its digest from the registry rather than typing one
400 BAD_REQUESTon submit, about registry credentials
- Cause
- The submission carried registry pull credentials, which are refused at every tier
- What to do
- Remove them and make the image pullable without authentication. An image the platform cannot fetch anonymously cannot be published
400 BAD_REQUESTon a new provider submit, about the owning group
- Cause
- The
privatetier was asked for with no group, with a group that is not a top-level one, or a group was named at a tier other thanprivate - What to do
- Name a top-level group when the tier is
private, and leave the field out otherwise. On a later version the field is ignored — the owning group is whatever the provider was published to
403 FORBIDDENor 404 NOT_FOUND on a private submit, naming the group
- Cause
- You are below Maintainer in the group you named, or the group id does not resolve for you
- What to do
- Ask an Owner there to make you a Maintainer, or take the id from the group's own page. A group you cannot see is reported as absent, not as forbidden
400 BAD_REQUESTon submit, about the contract version
- Cause
- The manifest declared a contract version other than
1 - What to do
- Set it to
1. That is the only manifest contract the platform accepts
400 BAD_REQUESTon submit, about the version
- Cause
- The version is not a leading
vplus three numbers - What to do
- Write it as
v1.4.0. A pre-release suffix, build metadata, a range orlatestare all refused
400 BAD_REQUESTon any call, naming a field nobody recognises
- Cause
- A field name is misspelt, or belongs to a body you were not sending
- What to do
- Every request body refuses fields it does not know rather than ignoring them, so the name in the message is the typo. Delete it or correct the spelling
400 BAD_REQUESTon a version submit, about the provider id
- Cause
- The manifest's provider id does not match the one in the URL
- What to do
- Make them the same. A version submit cannot rename or retarget the provider
409 CONFLICTon submit, saying the provider exists
- Cause
- You used the new-provider call for a provider that is already in the catalog
- What to do
- Use the new-version call on that provider instead
409 CONFLICTon submit, saying a submission is open
- Cause
- Another submission for the same provider and version is still awaiting a decision
- What to do
- Withdraw it, or wait for it to finish, then submit again. The version slot frees when that submission reaches a final state, not when it is approved
409 CONFLICTon submit, saying the version is published
- Cause
- That version number has already published, and published versions are immutable
- What to do
- Bump the version and submit again
409 CONFLICTon submit, about the tier
- Cause
- The tier you asked for is not the tier the provider already has
- What to do
- Submit at the provider's existing tier. Tier is fixed at first publication
429 TOO_MANY_REQUESTSon submit
- Cause
- Twenty of your submissions are open — awaiting review, being checked, or being mirrored
- What to do
- List your provider's submissions, withdraw the ones you no longer want, and submit again. Nothing was created by the refused call
409 CONFLICTon withdraw
- Cause
- The submission has been approved, is being mirrored or signed, or has already reached a final state
- What to do
- There is nothing to withdraw. Let it finish and submit a corrected version, or read the submission to see which final state it landed in
409 CONFLICTon restoring a version
- Cause
- The version was sunset by the platform, not deprecated by you
- What to do
- A sunset is not reversible by anyone. Publish a replacement version and move consumers onto it; the version carries the date its grace window ends, and that is your deadline
403 FORBIDDENon a submission read, a lifecycle change, or a publisher edit
- Cause
- Your publisher does not own that provider or that submission, or the publisher id in the path is somebody else's
- What to do
- Only the owning publisher and platform staff read review notes, change a version's lifecycle, or edit and verify a publisher. Check you are calling as the identity that registered the publisher
403 INSUFFICIENT_SCOPEon any call
- Cause
- The token is valid but was minted without the scope this call needs
- What to do
- Mint a token carrying
publisher:manageorprovider_publishing:writeas appropriate. Scopes are chosen when a token is created and cannot be added afterwards
401 UNAUTHORIZEDon any call
- Cause
- No credential was sent, or the one sent has expired or been revoked
- What to do
- Sign in again, or replace the token. Every publishing call needs an authenticated caller — none of them is public
404 NOT_FOUNDon a version submit or a submissions list
- Cause
- Nothing has ever published under that provider id, so there is no catalog entry to add a version to or to list submissions for
- What to do
- Check the id, including the namespace prefix. If this really is the provider's first version, use the new-provider call instead
404 NOT_FOUNDon a submission read or a withdraw
- Cause
- The submission id is wrong, or belongs to a submission that no longer exists
- What to do
- Take the id from the response the submit call gave you, or from the provider's submission list
404 NOT_FOUNDon deprecate or restore
- Cause
- The provider id is unknown, or that version never published
- What to do
- Check both parts of the path. Only a published version has a lifecycle; a submission still in flight has a state instead
The submission sat in submitted for days
- Cause
- It is at the
verifiedtier and is waiting for a human reviewer - What to do
- Nothing to do from your side. Check the contact address on your publisher profile is one you read, since that is where a reviewer's questions go. If you meant to publish without a review, withdraw it and resubmit at
community
The submission ended in changes_requested
- Cause
- A reviewer asked for something to change
- What to do
- Read the notes on the submission, fix the manifest, and submit again. The version number is free the moment changes are requested, so you can reuse it
The submission ended in verification_failed
- Cause
- The digest you attested did not resolve at your registry, or the image is not pullable without credentials
- What to do
- Confirm that pulling the image by digest works from outside your network, then submit a new submission with a digest read from the registry itself
The submission ended in mirror_failed
- Cause
- The copy-and-sign step could not complete — usually the image disappeared from your registry, or its pushed digest did not match what you attested
- What to do
- Re-push the image, confirm the digest, and submit again. Deleting or re-tagging the source image between submitting and approval is the common cause
A consumer's pipeline reports an image digest mismatch
- Cause
- The bytes behind the reference moved after the version published
- What to do
- Publish a new version. A published version is pinned to a digest on purpose, so replacing bytes behind a tag is never picked up
Consumers see a warning when a pipeline uses a deprecated version
- Cause
- You deprecated that version, and validating a pipeline that names it now warns
- What to do
- Expected, and non-blocking. Publish the replacement version and point consumers at it; the warning is what nudges them across
A community provider is published but an organisation cannot install it
- Cause
- Organisations do not accept the community tier until somebody there turns it on
- What to do
- Ask an Owner there to accept community providers in the organisation's provider policy. Verifying your namespace also helps, since a policy set to verified publishers only will admit you once the badge is there
A private provider is invisible to the people you expected
- Cause
- A private provider belongs to the one top-level group you named at submission, and nobody outside it sees it
- What to do
- Check you named the right group. To reach a second organisation, publish at
communityinstead — a provider's tier and owner cannot be changed after its first version publishes
FAQ
Can I change my namespace?
No. It prefixes every provider id you have ever shipped, so changing it would break every pipeline pinned to one of them. Register a second publisher from a different identity if you need a different namespace.
Can I edit a version after it publishes?
No. A published version is immutable, including its release notes. Publish a new version; deprecate the old one if you want people to move.
Can I rename the provider, or change what a connection to it needs?
No. The provider's name, description and connection shape are taken from the first version that publishes, and a later version's provider block is ignored. If the credentials your provider needs genuinely change, get in touch rather than shipping a version that silently keeps the old shape.
Does deprecating a version break running pipelines?
No. A deprecated version still resolves and still runs. Validating a pipeline that names it raises a non-blocking warning, and the version shows as deprecated wherever versions are listed.
Can a service account publish?
Yes, and for anything automated it should. A publisher binds to whichever identity registered it, so a release pipeline that publishes needs its own publisher, registered from its own service account. That is two scopes, not one: publisher:manage on the token that registers the publisher, and provider_publishing:write on the token that submits.
Why does my image reference disappear from the published capability?
Only at the verified tier, and only because we publish our own signed copy. Your original reference is kept alongside it for the record. At community and private the published reference is yours, untouched.
Do I have to verify my namespace?
Only for the verified tier. Verification is also what lights the badge that an organisation running a verified-publishers-only policy is looking for, so it widens who can install what you ship even at the community tier.
Related
- Using providersinstalling a provider into a group, connecting credentials to it, and pinning a version in a pipeline
- Provider policy and administrationhow an organisation decides which tiers it accepts, and what platform staff can do to a published provider
- Providerswhere the provider model starts, if you landed here before deciding to publish
- Pipelineshow a capability you publish is wired into a pipeline and run
- Access tokensminting a token with the publishing scopes, and what a service account token can carry
- Service accountsthe identity a release pipeline registers its own publisher from