REST API (7)
Last updated Oct 22, 2026 Use the Artifacts REST API to manage repos, remotes, forks, imports, and tokens from external systems.
Review Namespaces first, then choose the namespace name you will use in these API paths.
Base URL and authentication
Artifacts REST routes use this base path:
https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/artifacts/namespaces/$ARTIFACTS_NAMESPACE
Requests use Bearer authentication:
Authorization: Bearer $CLOUDFLARE_API_TOKEN
Route paths below are shown relative to /accounts/$ACCOUNT_ID. Curl examples use ARTIFACTS_BASE_URL or ARTIFACTS_ACCOUNT_BASE_URL to keep commands shorter.
Token types
Cloudflare API tokens authenticate REST control-plane routes. Repo tokens authenticate Git operations against the returned remote URL.
The following examples assume:
export ACCOUNT_ID="<YOUR_ACCOUNT_ID>"
export ARTIFACTS_NAMESPACE="default"
export ARTIFACTS_REPO="starter-repo"
export CLOUDFLARE_API_TOKEN="<YOUR_API_TOKEN>"
export ARTIFACTS_BASE_URL="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/artifacts/namespaces/$ARTIFACTS_NAMESPACE"
export ARTIFACTS_ACCOUNT_BASE_URL="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/artifacts"
All JSON responses use the standard Cloudflare v4 envelope:
{
"result": {},
"success": true,
"errors": [],
"messages": []
}
Successful blob, file, and raw responses return file bytes directly instead of JSON. For example, GET /artifacts/namespaces/:namespace/repos/:name/file?ref=main&path=README.md returns the contents of README.md with Content-Type: application/octet-stream. Error responses still use the standard envelope:
{
"result": null,
"success": false,
"errors": [
{
"code": 10200,
"message": "File not found"
}
],
"messages": []
}
Returned repo tokens are secrets. Do not log them or store them in long-lived remotes unless your workflow requires it.
Shared types
export type NamespaceName = string;
export type Jurisdiction = "eu" | "us";
export type RepoName = string;
export type BranchName = string;
export type Scope = "read" | "write";
export type TokenState = "active" | "expired" | "revoked";
export type ArtifactToken = string;
export type Cursor = string;
export type RepoSortField =
| "created_at"
| "updated_at"
| "last_push_at"
| "name";
export type SortDirection = "asc" | "desc";
export interface ApiError {
code: number;
message: string;
documentation_url?: string;
source?: {
pointer?: string;
};
}
export interface CursorResultInfo {
cursor: string;
per_page: number;
count: number;
}
export interface OffsetResultInfo {
page: number;
per_page: number;
total_pages: number;
count: number;
total_count: number;
}
export type ResultInfo = CursorResultInfo | OffsetResultInfo;
export interface ApiEnvelope<T> {
result: T | null;
success: boolean;
errors: ApiError[];
messages: ApiError[];
result_info?: ResultInfo;
}
export interface RepoInfo {
id: string;
name: RepoName;
description: string | null;
default_branch: string;
created_at: string;
updated_at: string;
last_push_at: string | null;
source: string | null;
read_only: boolean;
}
export interface RepoWithRemote extends RepoInfo {
remote: string;
}
export interface TokenInfo {
id: string;
scope: Scope;
state: TokenState;
created_at: string;
expires_at: string;
}
Namespaces
Create a namespace
Route: POST /artifacts/namespaces
Use the account-level base URL.
Request body:
namespaceNamespaceNamerequiredjurisdiction"eu" | "us"optional (default: unrestricted)
curl --request POST "$ARTIFACTS_ACCOUNT_BASE_URL/namespaces" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"namespace": "my-eu-namespace",
"jurisdiction": "eu"
}'
The jurisdiction applies to every repo in the namespace and cannot be changed after creation. If you omit jurisdiction, Artifacts creates an unrestricted namespace.
For more information, refer to Data localization.
List namespaces
Route: GET /artifacts/namespaces?limit=&cursor=
Use the account-level base URL.
curl "$ARTIFACTS_ACCOUNT_BASE_URL/namespaces?limit=20" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
Get a namespace
Route: GET /artifacts/namespaces/:namespace
curl "$ARTIFACTS_ACCOUNT_BASE_URL/namespaces/$ARTIFACTS_NAMESPACE" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
Delete a namespace
Route: DELETE /artifacts/namespaces/:namespace
The namespace must be empty before you delete it.
curl --request DELETE "$ARTIFACTS_ACCOUNT_BASE_URL/namespaces/$ARTIFACTS_NAMESPACE" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
The API returns the following response status codes:
| Status | Description |
|---|---|
204 |
Namespace deleted. |
404 |
Namespace is missing or already deleted. |
409 |
Namespace contains repositories, or activation or deletion is in progress. |
Repos
Create a repo
Route: POST /artifacts/namespaces/:namespace/repos
Request body:
nameRepoNamerequireddescriptionstringoptionaldefault_branchBranchNameoptionalread_onlybooleanoptional
Response type:
export interface CreateRepoRequest {
name: RepoName;
description?: string;
default_branch?: BranchName;
read_only?: boolean;
}
export interface CreateRepoResult {
id: string;
name: RepoName;
description: string | null;
default_branch: string;
remote: string;
token: ArtifactToken;
}
export type CreateRepoResponse = ApiEnvelope<CreateRepoResult>;
curl --request POST "$ARTIFACTS_BASE_URL/repos" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"name": "starter-repo",
"description": "Repository for automation experiments",
"default_branch": "main",
"read_only": false
}'
{
"result": {
"id": "repo_123",
"name": "starter-repo",
"description": "Repository for automation experiments",
"default_branch": "main",
"remote": "https://<ACCOUNT_ID>.artifacts.cloudflare.net/git/default/starter-repo.git",
"token": "art_v1_0123456789abcdef0123456789abcdef01234567?expires=1760000000"
},
"success": true,
"errors": [],
"messages": []
}
Create, fork, and import responses return the token string only. The token encodes its expiry directly in the ?expires= suffix. The separate POST /tokens route also returns expires_at alongside the plaintext token.
List repos
Route: GET /artifacts/namespaces/:namespace/repos?limit=&cursor=&search=&sort=&direction=
Query parameters:
limitnumberoptional (default: 50, max: 200)cursorCursoroptionalsearchstringoptionalsort"created_at" | "updated_at" | "last_push_at" | "name"optional (default: "created_at")direction"asc" | "desc"optional (default: "desc")
Response type:
export interface ListReposQuery {
limit?: number;
cursor?: Cursor;
search?: string;
sort?: RepoSortField;
direction?: SortDirection;
}
export type ListReposResponse = ApiEnvelope<RepoWithRemote[]>;
curl "$ARTIFACTS_BASE_URL/repos?limit=20&sort=updated_at&direction=desc" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
{
"result": [
{
"id": "repo_123",
"name": "starter-repo",
"description": "Repository for automation experiments",
"default_branch": "main",
"created_at": "<ISO_TIMESTAMP>",
"updated_at": "<ISO_TIMESTAMP>",
"last_push_at": "<ISO_TIMESTAMP>",
"source": null,
"read_only": false,
"remote": "https://<ACCOUNT_ID>.artifacts.cloudflare.net/git/default/starter-repo.git"
}
],
"success": true,
"errors": [],
"messages": [],
"result_info": {
"cursor": "next-cursor",
"per_page": 20,
"count": 1
}
}
Get a repo
Route: GET /artifacts/namespaces/:namespace/repos/:name
Response type:
export type GetRepoResponse = ApiEnvelope<RepoWithRemote>;
curl "$ARTIFACTS_BASE_URL/repos/$ARTIFACTS_REPO" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
{
"result": {
"id": "repo_123",
"name": "starter-repo",
"description": "Repository for automation experiments",
"default_branch": "main",
"created_at": "<ISO_TIMESTAMP>",
"updated_at": "<ISO_TIMESTAMP>",
"last_push_at": "<ISO_TIMESTAMP>",
"source": null,
"read_only": false,
"remote": "https://<ACCOUNT_ID>.artifacts.cloudflare.net/git/default/starter-repo.git"
},
"success": true,
"errors": [],
"messages": []
}
Delete a repo
Route: DELETE /artifacts/namespaces/:namespace/repos/:name
This route returns 202 Accepted.
Response type:
export interface DeleteRepoResult {
id: string;
}
export type DeleteRepoResponse = ApiEnvelope<DeleteRepoResult>;
curl --request DELETE "$ARTIFACTS_BASE_URL/repos/$ARTIFACTS_REPO" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
{
"result": {
"id": "repo_123"
},
"success": true,
"errors": [],
"messages": []
}
Fork a repo
Route: POST /artifacts/namespaces/:namespace/repos/:name/fork
Request body:
