API Errors
Every error from the Vendor API (/api/v1/vendor) and the Customer API (/api/v1/customer) has the same shape: an RFC 9457 problem details object, served as application/problem+json.
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
{
"type": "https://www.breakwaterapp.com/docs/api/errors#insufficient_scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This needs a token with the write scope.",
"code": "insufficient_scope",
"required_scope": "write"
}
| Member | What it is |
|---|---|
code |
The error, as a short machine-readable string. Branch on this. The codes are listed below and don't change |
type |
The code's section on this page, as a URL. It always agrees with code |
title |
A short label for the code. It's the same every time |
status |
The HTTP status, repeated |
detail |
A sentence for a person, about this request. Show it, but don't branch on it: its wording can change |
Some codes add members of their own, listed with each code. Clients should ignore members they don't recognize.
The MCP endpoint and the OAuth endpoints answer in the formats their own standards require, and the registry answers Docker clients in the registry's own format, so none of them use problem details.
Validation errors
A 422 with validation_failed lists each problem in errors:
{
"type": "https://www.breakwaterapp.com/docs/api/errors#validation_failed",
"title": "Validation failed",
"status": 422,
"detail": "Name can't be blank and Slug has already been taken.",
"code": "validation_failed",
"errors": [
{"field": "name", "detail": "Name can't be blank"},
{"field": "slug", "detail": "Slug has already been taken"}
]
}
field is the request parameter the message is about, under the resource's key (customer[name] is name). It's left out when a message isn't about one parameter, such as a problem with one of a release's images. detail on each error is the full message, and the top-level detail has all of them in one sentence.
Codes
invalid_token
401. The credential is missing, malformed, unknown, expired or revoked, or its owner is no longer on the vendor. The detail doesn't say which. On the Vendor API, send a vapi_ token as Authorization: Bearer; on the Customer API, a customer pull token (ctok_...) as HTTP Basic credentials. See API Authentication.
registry_token_not_accepted
401. You sent Basic auth or a vtok_/ctok_ registry token to the Vendor API. Create an API token in the vendor portal under API tokens instead.
subscription_required
403. The vendor has no active Breakwater subscription. An owner can fix it on the billing page.
two_factor_required
403. The vendor requires two-factor authentication and the token's owner hasn't set it up. They can set it up in the vendor portal.
sso_required
403. The vendor requires single sign-on and the token's owner has no identity on it. They need to sign in through the identity provider in the vendor portal once.
insufficient_scope
403. The token doesn't have the scope the request needs. required_scope is read or write. Create a token with that scope.
forbidden
403. The token's owner doesn't have a role that allows this. When one role would, required_role names it (for example member). Ask an owner or admin to change the role.
plan_required
403. The vendor's plan doesn't include the feature this endpoint is part of. feature names it: air_gapped_bundles or instance_telemetry. An owner can change the plan.
customer_limit_reached
403. Creating the customer would go over the plan's customer limit. limit is how many customers the plan includes and count how many the vendor has. Remove a customer or change the plan.
not_found
404. Nothing is at this address: an unknown path, or a record that doesn't exist or belongs to someone else. The Customer API answers the same way for a release the customer can't see, so a 404 never confirms that a product or release exists.
parameter_missing
400. The request body is missing the resource's key. parameter names it, for example customer for {"customer": {"name": "Acme"}}.
malformed_request
400. The request body isn't valid JSON.
validation_failed
422. The request was understood but its values aren't valid. See Validation errors. When a release was published and then withdrawn because an image changed while it was publishing, release_id names the withdrawn release.
license_key_not_issuable
422. A license key can't be issued for this license, for example because it's cancelled. The detail says why.
endpoint_disabled
422. The webhook endpoint is disabled, so it can't receive a test event. Enable it first.
invalid_transition
409. The release can't change to that status from the one it's in, for example withdrawing a release that's already withdrawn.
release_not_withdrawn
409. Only a withdrawn release can be deleted. Withdraw it first.
bundle_already_completed
409. The bundle event is already completed, so it can't be completed again.
rate_limited
429. Too many requests. Wait for the number of seconds in the Retry-After header, which retry_after repeats, then try again. See Rate Limiting.