Introduction
The Admin API lets you manage regular API keys and inspect billing data for a personal account or organization. Organization admin keys also provide access to container-related APIs. Personal admin keys access only the creator’s personal resources, even if the creator belongs to an organization. Organization admin keys access only their organization. Select Personal or the target organization in the dashboard before creating a key; organization keys require an admin role in that organization. Creating an admin key requires a signed-in user session, not another admin key. It does not require active API billing, but creating regular inference keys still requires active token billing for the account. Existing personal admin keys remain valid.Common use cases
- Automating key management: Create, rotate, or delete API keys programmatically, and set expiration dates or token limits based on your business logic.
- Building custom dashboards: Display usage metrics, cost breakdowns by model, and historical trends using the billing and time-series endpoints.
- Monitoring usage: Query aggregated or per-key usage statistics to track costs and token consumption.
- Managing containers programmatically: Create, validate, deploy, update, stop, and delete instances. Inspect and update projects, wrap model weights, manage repository config and release builds, and manage org secrets, SSH keys, custom domains, and registry credentials.
Authentication
Admin API keys provide programmatic access to your account resources. Admin keys are prefixed withadmin_ and must be included in the Authorization header as a Bearer token.
Need to create an admin API key? Follow our step-by-step guide: Getting a Tinfoil Admin Key
Available endpoints
This page covers the following endpoints:Caller identity
GET /api/auth/context- Inspect the authenticated personal or organization context
Infrastructure limits
GET /api/infrastructure/limits- Get the account’s infrastructure tier, limits, and usage
API key management
GET /api/keys- List API keysPOST /api/keys- Create a new API keyPOST /api/keys/update- Update an API key’s name, expiration, limits, or notificationsDELETE /api/keys/:key- Delete an API key
Billing & usage
GET /api/billing/usage- Get aggregated usage statistics for all keysPOST /api/billing/usage/key- Get usage statistics for a specific keyGET /api/billing/time-series- Get time series dataGET /api/billing/requests- List individual requestsGET /api/billing/transactions- Get transaction history
Container endpoints
- Lifecycle & deployment
POST /api/containers/validate-name- Validate a container name or custom domain before deployPOST /api/containers/validate- Validatetinfoil-config.ymlbefore deployGET /api/containers/hosts- List hosts available to the organizationGET /api/containers- List containersGET /api/containers/:id- Get a specific containerPOST /api/containers- Create a containerPOST /api/containers/:id/update/plan- Preview a running instance’s update without deployingPOST /api/containers/:id/update- Update a running container to a new tag or configurationPOST /api/containers/:id/stop- Stop a running containerPOST /api/containers/:id/deploy- Deploy a stopped or failed containerDELETE /api/containers/:id- Delete a containerGET /api/containers/:id/update- Get in-progress update statusPOST /api/containers/:id/update/promote- Switch traffic to a held update that is readyPOST /api/containers/:id/update/cancel- Cancel an in-progress updatePOST /api/containers/:id/github-connection- Toggle GitHub App connectionGET /api/containers/:id/metrics- Get resource metrics
- Projects
GET /api/containers/projects- List projects with instance countsPATCH /api/containers/projects/:id- Update project settingsPOST /api/containers/projects/:id/update/plan- Preview updates for all or selected instances without deployingPOST /api/containers/projects/:id/update- Update all or selected instances
- Model weights
POST /api/models/wrap- Start a model wrap jobGET /api/models/wrap- List wrap jobsGET /api/models/wrap/:host/:job_id- Get wrap job statusDELETE /api/models/wrap/:host/:job_id- Delete a wrap job
- GitHub repository operations
GET /api/github/repos/:owner/:repo/config- Readtinfoil-config.ymlPOST /api/github/repos/:owner/:repo/config/pr- Open a config pull requestGET /api/github/repos/:owner/:repo/pulls/:number- Get pull request stateGET /api/github/repos/:owner/:repo/build/info- Get release and tag infoGET /api/github/repos/:owner/:repo/build/status- Get release workflow run statusPOST /api/github/repos/:owner/:repo/build- Dispatch a release build
- Related resources
GET /api/secrets- List org secretsPOST /api/secrets- Create an org secretGET /api/secrets/:name- Get a secret’s metadataPUT /api/secrets/:name- Update a secretDELETE /api/secrets/:name- Delete a secretGET /api/repositories/:owner/:repo/secrets- List repository secretsPOST /api/repositories/:owner/:repo/secrets- Create a repository secretGET /api/repositories/:owner/:repo/secrets/:name- Get repository secret metadataPUT /api/repositories/:owner/:repo/secrets/:name- Update a repository secretDELETE /api/repositories/:owner/:repo/secrets/:name- Delete a repository secretGET /api/ssh-keys- List org SSH keysPOST /api/ssh-keys- Create an SSH keyDELETE /api/ssh-keys/:name- Delete an SSH keyGET /api/domains- List custom domainsPOST /api/domains- Add a domainPOST /api/domains/:domain/verify- Verify a domainDELETE /api/domains/:domain- Delete a domainGET /api/registry-credentials- List private registry credential statusPUT /api/registry-credentials/:registry- Create or update private registry credentialsDELETE /api/registry-credentials/:registry- Delete private registry credentials
API Key Management
The response examples in this section use organization admin keys. Personal inference-key responses omitis_owner.
List API Keys
Example Request
Response
1000000000 = $1.00).
Create API Key
Request Body
Name for the API key. Must contain only alphanumeric characters, hyphens, underscores, spaces, and periods.
Maximum spend in dollars for this key. Must be at least
0.01. Omit or pass 0 for no cost limit.429 and code: insufficient_quota. They are separate from the account usage limit, which resets each billing period: raising a key’s cap does not lift a usage-limit pause. See Quota errors.
Example Request
Response
Update API Key
Request Body
New expiration as an RFC 3339 timestamp in the future. Pass
null to make the key non-expiring. Omit to leave it unchanged.Example Request
Response
Delete API Key
Path Parameters
Example Request
Response
Billing & Usage
How usage is recorded
Each inference request produces one usage record after the model’s response completes. The record contains the model, the input, cached input, and output token counts reported by the model, and a timestamp. It never contains prompt or response content. Within a few seconds of completion, the record is reflected in the per-key counters, the current billing period total, and the usage endpoints below. See Usage reporting and billing for how these records leave the router enclave. The admission check for a new request compares the recorded usage against the account usage limit and any per-key caps. Requests that are still in progress have not produced a record yet, so they do not count. Limits are therefore soft:- A long or concurrent batch of requests admitted just before a limit is reached completes and is billed in full, even if the total then exceeds the limit.
- After the limit is crossed, the next request on that key is rejected with
429or402andcode: insufficient_quota. See Quota errors. The delay between the crossing request completing and new requests being rejected is a few seconds. - The expected overshoot is roughly the cost of whatever was in flight when the limit was reached. Keep concurrency and per-request
max_tokensbounded if you need a tight ceiling.
Streaming, failed, and interrupted requests
- A streaming response is billed for the prompt and for the output tokens generated up to the point the stream ended. A client disconnect part way through produces a record for the tokens generated so far, not for the full generation.
- Requests the model rejects with a
4xxor5xxstatus, and requests that fail before the model starts responding, produce no usage record and no charge. - A completed non-streaming response that reports no usage is recorded as one request with zero tokens.
- Requests rejected by rate limiting or usage limits before reaching a model are not charged.
What the token counts include
input_tokensis the full prompt, including the portion served from the prompt cache.cached_input_tokensis the subset that was a cache hit and is priced at the cached-input rate when the model has one. See Prompt caching.output_tokensincludes any reasoning tokens the model generated. Reasoning is not metered separately and is billed as output.- Web search and privacy filter usage are recorded as separate per-request fees and appear in the per-request
X-Tinfoil-Usage-Metricssummary. See Usage metrics.
Reconciling usage per request
For reconciliation at request granularity, capture the response-side usage at call time: either theusage object in the API response, or the X-Tinfoil-Usage-Metrics header or trailer, which includes cost_usd when every component is known. Store it against your own request identifier. The server-side record does not carry a client-supplied identifier, so List Requests matches rows by timestamp, key, model, and token counts rather than by request ID.
Get Usage Statistics
Query Parameters
Time preset for usage statistics. If omitted, returns all-time usage. Valid values:
5m, 15m, 30m, 1h, 24h, today, 7d, 30d, 60d, 90d, 180d, 365d, 3mo, 6mo, 12mo, all, and period (the account’s current token billing period).RFC 3339 end of an explicit range. Must be paired with
start. Future values are clamped to now. The range cannot exceed two years.Example Request
Response
keys object is grouped by API key name. cached_input_tokens counts the subset of input_tokens served from prompt cache.
Get Usage by Key
Query Parameters
Accepts the sametime, start, and end parameters as GET /api/billing/usage. If omitted, returns all-time usage.
Request Body
The API key value to query (for example,
tk_your_full_key_value_here), as returned by GET /api/keys.Example Request
Response
Get Time Series Data
Query Parameters
Time preset for the time series. Accepts the same presets as
GET /api/billing/usage, including period. Explicit start and end parameters are also supported.5m → 5s, 15m → 15s, 30m → 30s, 1h → 1m, 24h → 15m, 7d → 2h, 30d → 8h, 365d → 24h, and longer ranges → 168h. Empty buckets are included as zero-value data points.
Example Request
Response
List Requests
Query Parameters
RFC 3339 end of the range. Future values are clamped to now. The range cannot exceed two years.
Example Request
Response
next_cursor is omitted on the last page. id is the server-side record identifier; it is not returned on the inference response. For a full export, use GET /api/billing/usage/export/raw with the same start and end parameters, which returns a CSV with columns time, api_key, api_key_name, model, tokens, prompt_tokens, cached_prompt_tokens, completion_tokens, capped at 100,000 rows.
Get Transaction History
Viewing organization transaction history requires organization admin access. If the account has no billing history yet, the response is:
Example Request
Response
Authenticated context
GET /api/auth/context returns the effective caller identity without listing organization resources. It is available to authenticated organization and personal admin keys, including scoped keys without container-host permissions. The CLI uses it for login and whoami.
The response contains user_id, context_type (organization or personal), and organization: an object containing id for organization context, or null for personal context. It does not return secret key material or an organization name. Use the returned context rather than inferring an organization from the key string.
Infrastructure limits
Returns the infrastructure tier of the key’s organization or personal account, the limits that apply, and what currently counts against them. Scoped admin keys need the
infrastructure.limits.read action.Example Request
Response
owner_type:organizationoruser.tier: the current tier number.tier_sourcesays what sets it:spend(lifetime paid total),gpu_access(an organization with GPU access), orpinned(set by Tinfoil).lifetime_paid_dollars: the lifetime paid total, in US dollars.tiers: every tier with itstier,name,min_lifetime_paid_dollars,max_containers,max_sandboxes, andmax_storage_bytes.limits: themax_containers,max_sandboxes, andmax_storage_bytesthat apply. They can be higher than the tier’s values when Tinfoil has raised the account’s limits.usage: thecontainers,sandboxes, andstorage_bytesthat currently count.sandboxes_enabled: whether the account can create bunkers.workspace_sizes_gbanddefault_workspace_size_gb: the bunker workspace sizes you can choose and the default.
Containers
Organization admin API keys can access the same container APIs as a browser session, as long as the key belongs to the target organization. Personal admin keys cannot access these organization-only resources.Create, deploy, and update operations require an active container subscription. Read-only endpoints and cleanup operations such as list, get, stop, and delete do not require an active subscription.
Private registry endpoints require private registry access to be enabled for the organization.
Scoped admin keys
Scoped admin keys use route action names in theirallowed_actions list. The public container-related actions are:
Container restrictions can set allowed
owner/repo values and a container-name regular expression. Container lists return only matches, while reads and lifecycle operations reject nonmatching containers. Creates check the requested repository and name. A create request with replace_container_id also requires containers.delete access to the container being replaced.
The CLI performs auxiliary requests. Creating an instance requires containers.validate as well as containers.create: the CLI validates the configuration and mount assignments before creating anything. It sends the proposed name as instance_name, so name-restricted keys can validate a matching create without gaining access to other names. Container commands that resolve an ID or name require containers.read in addition to the operation’s action; name resolution uses the container list. Project get, settings, and update commands resolve through the project list and therefore require containers.projects.read. Update planning uses the same action permission as the corresponding update. tinfoil login and tinfoil whoami use authenticated context, not the hosts route.
Repository-level operations cannot always narrow access to one container name. Project routes are visible or allowed only when the repository matches and every existing instance matches the name restriction. Repository-secret and GitHub repository operations reject name-restricted keys. A key with any container repository or name restriction is also denied organization-wide container runtime status, organization secrets, SSH keys, custom domains, registry credentials, and model weights, even if containers.runtime-status.read, secrets.read, secrets.write, ssh-keys.read, ssh-keys.write, domains.read, domains.write, registry-credentials.read, registry-credentials.write, or any models.* action is listed.
Some endpoints have no action name and are reachable only by unscoped admin keys. A scoped key receives 403 Forbidden on all API key management routes (/api/keys), on the billing usage, time-series, and transaction routes, and on POST /api/containers/validate-name. Use an unscoped admin key for key management and billing automation.
Lifecycle & Deployment
Validate Container Name
Request Body
Example Request
Response
Validate Container Config
Request Body
Proposed name when validating a new instance. Required for a name-restricted
admin key when no existing instance ID is supplied. The name and repository
must both match the key’s restrictions. For update or replacement validation,
authorization uses the existing instance’s stored name and ownership, not this
proposed name.
Existing container UUID. When present, instance-limit checks are skipped for update validation.
Existing container UUID. When present, instance-limit checks are skipped for replace validation.
List Hosts
Returns only hosts surfaced to the organization. If a default host is
returned, it is marked
is_default. Each host’s available_gpu_values is
the intersection of the host’s capabilities and the organization’s GPU
entitlement. gpu_type and cpu_type describe the host’s hardware;
gpu_type is empty for hosts without GPUs. To deploy to a host, send its
name as host_name.Response
List Containers
Example Request
Response
Thecpus, memory_mb, and gpus response fields report the resources from
the measured tinfoil-config.yml in the published release. Lifecycle request
bodies do not override these resources.
Get Container
Path Parameters
Example Request
Create Container
Request Body
Saved per-instance environment variables as key-value pairs. These are
separate from measured
env values in tinfoil-config.yml.Names of existing Tinfoil-managed organization or repository secrets to inject.
For non-debug private-keyserver delivery, omit this field or send
[];
private names come from the measured config, not this selection.Target host name. Explicit selection requires host-selection access, and the
host and requested GPU shape must be available to the organization.
Existing container UUID to replace. The old instance is stopped and removed
before the replacement is queued. A later replacement failure does not
restore the old instance.
Whether to mark an initial or changed selected tag as the repository’s latest
GitHub release once the container is running and serving production. Applies
only to a non-debug instance with a connected GitHub App and an active
repository. Defaults to
true.Example Request
Response
Without declared volume mounts, returns the created instance with statusdeploying; it transitions to running once deployment completes. With declared mounts, returns stopped and volume_slots, the named mounts from the config. Attach the selected disks, then call Deploy. A disk is required only for a mount with key_secret; optional mounts may remain empty. See persistent volumes (Beta).
Every container object carries update_strategy, either blue_green or replace, for its saved configuration. Update requests also check the target release, which may require a different strategy.
The connections object contains production and review, each either null when unavailable or an object with url, repo, and tag. Review describes the current blue/green candidate, including its review port and target tag, not the production connection. Use these values with a verified client; merely opening the URL is not attestation verification. A connection descriptor does not make a debug enclave confidential.
Plan an update
POST /api/containers/:id/update/plan accepts the same body as Update Container and returns a read-only current-to-target plan without deploying. It requires containers.update and the same organization, repository, and instance-name authorization as execution.
The plan includes read_only, instance_id, name, current and target resource objects (tag, cpus, memory_mb, gpus), update_strategy, downtime_required, hold, hold_source (project or request), hold_available, mark_latest_release, and volume_data (retained). configuration_changes reports added, changed, and removed names for variables, secrets, and ssh_keys, changed settings, and secrets_refreshed; values are not exposed. cost_estimate.available indicates whether an estimate is available, with reason when it is not.
A plan can report an unavailable hold or required downtime without requiring consent to inspect it. Execution still requires a valid hold choice and downtime consent, and revalidates the request. The plan is not a reservation or a historical snapshot.
Secret delivery in update plans
Private keyserver delivery is beta because it depends on V3 attestation. Tinfoil-managed secret delivery is outside the V3 beta. When the target config has a nonemptykeyserver-url, an instance plan includes
the optional secret_delivery object. In a project plan it is nested at
results[].plan.secret_delivery, not at the project response’s top level.
The object is omitted for configs without keyserver-url; absence is not
evidence that external secrets have been verified.
For a non-debug private target, the object has this shape:
Both name lists are arrays, never
null. The object contains no endpoint URL,
secret values, or bootstrap credentials. Registry authentication, certificate
authorization, and debug SSH are separate from these workload-secret lists.
Request secrets always means a managed selection. On deploy/update, omitted or
null selections retain saved bindings; send "secrets": [] explicitly when
switching to private delivery.
Nonempty managed selections and private names supplied in saved/request
variables are rejected. There is no implicit fallback to managed values.
In config-validation responses, YAML keyserver-url appears at
config.keyserver_url, model key-secret at config.models[].key_secret,
volume key-secret at config.volumes[].key_secret, and dummy attestation at
config.shim.dummy_attestation. Invalid keyserver URL syntax returns a validation
error with field: "keyserver_url" and code: "invalid_keyserver_url".
See private-secret requirements.
Update Container
Replaces the version a
running container serves with a new tag, updated configuration, or both. The server evaluates the target release and volume configuration to choose the strategy. For a blue/green update (single GPU or CPU-only, no persistent volumes), the new version boots alongside the current one and traffic switches once it is ready. For a replacement update (multi-GPU or persistent volumes), the running instance stops first and the new version deploys in its place; the request must carry confirm_downtime: true and cannot be held for review. Containers in any other status return 409 with code INVALID_CONTAINER_STATE; use Deploy for stopped or failed containers.Path Parameters
Request Body
All fields are optional. Omitted fields keep their current values, exceptmark_latest_release, which defaults to true for each operation, and hold,
which inherits the project’s current hold_by_default setting.
New saved per-instance environment variables. This replaces the full saved
map, but not measured
env values in tinfoil-config.yml.New saved Tinfoil-managed secret selection. Omitted or
null retains the
saved selection. In managed delivery, a supplied list replaces it, then merges
with declared secrets that exist in repository or organization scope.
Non-debug private delivery requires an empty managed selection: send []
explicitly to clear existing bindings; declared private values stay in your keyserver.Hold the new version for review and promotion while the current instance keeps
serving production. Available only for a blue/green update; requesting a hold
when the target release requires replacement returns
400 with code HOLD_UNAVAILABLE.
Omit to inherit the project default; explicit true or false applies to this
operation only and does not modify project settings.Required to be
true when the target update requires replacement. Without it the
request returns 409 with code DOWNTIME_CONFIRMATION_REQUIRED and the update
is not started.For a changed tag, whether to mark the selected tag as the repository’s latest
GitHub release once the new version is running and serving production, or
after promotion for a held version. Applies only to a non-debug instance
with a connected GitHub App and an active repository. Defaults to
true.host_name.
A blue/green update of a single-GPU container needs a second free GPU on the container’s host for the new version. When none is free the request returns 409 with code HOST_GPU_BUSY and the running instance is untouched. CPU-only updates have no such requirement.
Example Request
Stop Container
Deploy Container
Boots a new enclave for a
stopped or failed container, optionally with updated settings. A stopping container queues the deploy behind the stop without changing its tag or host; wait until stopped to change either. Deploys go straight to production and cannot be held for review. A running container returns 409 with code INVALID_CONTAINER_STATE; use Update instead.Request Body
All fields are optional. Omitted fields keep their saved values, exceptmark_latest_release, which defaults to true for each operation.
Saved per-instance environment variables. This replaces the full saved map,
but not measured
env values in tinfoil-config.yml.New saved Tinfoil-managed secret selection. Omitted or
null retains the
saved selection. In managed delivery, a supplied list replaces it, then merges
with declared secrets that exist in repository or organization scope.
Non-debug private delivery requires an empty managed selection: send []
explicitly to clear existing bindings; declared private values stay in your keyserver.Deploy the container on a different available host. Host access and GPU
entitlement are rechecked. Attached volumes must be on the target host.
For a changed tag, whether to mark the selected tag as the repository’s latest
GitHub release once the container instance is running and serving production.
Applies only to a non-debug instance with a connected GitHub App and an active
repository. Defaults to
true.Delete Container
Returns
204 No Content on success.
Get Update Status
Response
pending (queued), deploying (submitted and booting),
started (the enclave is running and startup checks are in progress), ready
(healthy and ready for traffic, or held and waiting for promotion), and failed (deployment
or startup checks failed).
Promote Update
Cancel Update
Toggle GitHub App Connection
Request Body
Get Container Metrics
Query Parameters
Time preset. Valid values:
5m, 15m, 30m, 1h, 24h, today, 7d, 30d, 60d, 90d, 180d, 365d, 3mo, 6mo, 12mo, all.data_points and interval. Utilization values are
average and maximum percentages for each bucket; memory totals report the
corresponding CPU or GPU capacity.
Projects
A project contains all container instances in an organization that use the same GitHub repository. It is created implicitly by the first instance or repository secret; there is no explicit project-create endpoint. New projects havehold_by_default=false, inherited by individual and batch updates when hold is omitted.
List Projects
deploying_count combines instances in pending, deploying, started, and
stopping states.
There is no separate GET /api/containers/projects/:id endpoint. Resolve a project by ID or repository from the list response.
Update Project Settings
Update Project Instances
To inspect the same selection before updating,POST /api/containers/projects/:id/update/plan accepts the request body below and uses containers.projects.update authorization. It returns read_only, project_id, latest_release_tag, eligible/skipped/failed counts, and per-instance results with instance_id, name, status (planned, skipped, or failed), plan, and error. A planned result contains the same instance plan; skipped and failed results explain why no plan is available.
Holding for review override for this request. Omit it to use
hold_by_default; an
explicit value overrides the saved default for this update only.For selected instances changing tags, whether to mark the selected tag as the
repository’s latest GitHub release once an instance is running and serving
production, or after promotion for held versions. Applies only to
non-debug instances with a connected GitHub App and an active repository.
Defaults to
true.Required to be
true when any eligible targeted instance requires replacement
for the target release. Without it the whole request returns 409 with
code DOWNTIME_CONFIRMATION_REQUIRED and lists the affected instances; no
instance is updated.hold is requested, the whole request returns 400 with code HOLD_UNAVAILABLE and lists the affected instances; no instance is updated.
After preflight, the response contains one result per targeted instance with status updating, skipped, or failed. Only running instances are updated; stopped and failed instances are skipped (bring them up with Deploy), as are instances with an update already in progress.
Example Request
Model Weights
These endpoints back thetinfoil model CLI commands. They wrap Hugging Face model weights into verified artifacts that GPU containers load with integrity checking. See Model weights for the workflow. Access requires a GPU entitlement or model view access for the organization.
Wrap Model
Request Body
Response
schema, image_digest, root_hash, offset, verity_uuid, and ended_at. Failed jobs include error.
List Wrap Jobs
Get Wrap Status
Delete Wrap Job
GitHub Repository Operations
These endpoints back thetinfoil repo CLI commands. They read and update a repository’s tinfoil-config.yml, and trigger and monitor release builds. The repository must be accessible through the organization’s GitHub App installation. Name-restricted scoped admin keys are denied.
Get Repository Config
Response
Open Config Pull Request
Request Body
Provide exactly one ofconfig or raw.
Structured config fields to apply. Managed keys are updated in place and unmanaged keys in the existing file are preserved.
Response
Get Pull Request
Get Build Info
Response
has_new_commits is false only when the default branch head is exactly the commit tagged by the latest release.
Get Build Status
Response
Dispatch Build
Response
Related Resources
Secrets
Organization and repository secrets are encrypted values injected into containers at deploy time. Secret names must beUPPER_SNAKE_CASE or kebab-case.
Request Body
UsePOST /api/repositories/:owner/:repo/secrets with the same request body to
create a repository-scoped secret. The corresponding repository GET, PUT,
and DELETE endpoints use
/api/repositories/:owner/:repo/secrets/:name.
SSH Keys
SSH key names must be kebab-case, for examplemy-deploy-key.
Request Body
Custom Domains
Request Body
Checks DNS records and updates the domain’s verification state. The certificate CNAME controls
verified; a verified domain with a matching _tinfoil TXT record (txt_record_host, txt_record_value) also stays verified. cname_configured and acme_challenge_configured report whether the traffic and certificate CNAMEs resolve to their targets. A DNS lookup failure other than a missing record leaves verified unchanged.Registry Credentials
Private registry credentials are supported forghcr, gcr, dockerhub, ecr, and custom. One ecr connection and one custom connection may be stored per organization, each identified by its hostname.
Path Parameters
Request Body
Forghcr:
gcr:
dockerhub:
ecr, a private registry hostname and a long-lived IAM access key pair. The region is derived from the hostname. Temporary credentials (ASIA… key IDs) are rejected.
custom, a bare public hostname and a username/secret pair. The registry must be reachable over HTTPS on port 443 with a publicly trusted certificate; any bearer-token service must share the hostname.
ecr or custom credentials validates them against the registry before storing. A 400 response carries an error message explaining whether the request was malformed or the registry rejected the credentials.
Error Responses
Example error response:
Container lifecycle codes. The
error message says what to do next; show it to the user as is.
Infrastructure limit codes. The
error message explains how to free capacity or get a higher limit; show it to the user as is.