Cloudflare Artifacts REST API client.
Wraps the control-plane endpoints documented at https://developers.cloudflare.com/artifacts/api/rest-api/ — repos (create / list / get / delete / fork / import) and repo tokens (create / list / delete).
Function and field names mirror upstream verbatim: create_repo
not mint_repo, default_branch not branch, plaintext not
secret. The struct shapes (Repo, Token) match the TypeScript
interfaces in the upstream docs so the two can be cross-referenced
directly.
Setup
new/1 returns a %Req.Request{} with base_url, bearer auth,
and a default user-agent already set:
client = Exgit.CloudflareArtifacts.new(
account_id: System.fetch_env!("CF_ACCOUNT_ID"),
namespace: "default",
api_token: System.fetch_env!("CF_API_TOKEN")
)Any extra options (e.g. :plug for testing, :retry, :receive_timeout)
are forwarded to Req.new/1.
Full lifecycle
{:ok, %Repo{remote: remote}} =
Exgit.CloudflareArtifacts.create_repo(client,
name: "starter-repo",
default_branch: "main"
)
{:ok, %Token{plaintext: token}} =
Exgit.CloudflareArtifacts.create_token(client,
repo: "starter-repo",
scope: "write",
ttl: 86_400
)
transport = Exgit.Transport.HTTP.new(remote,
auth: Exgit.Credentials.Artifacts.auth(token)
)
# ... push, fetch, clone via the existing transport ...
{:ok, _} = Exgit.CloudflareArtifacts.delete_repo(client, "starter-repo")Return shapes
Single-resource endpoints return {:ok, struct}. List endpoints
return {:ok, items, result_info} — result_info is the upstream
pagination map verbatim (cursor for repos, offset for tokens).
Errors:
{:error, %Req.Response{}}— non-2xx, or 2xx withsuccess: false. The parsed v4 envelope is inbody; the upstream error list lives atbody["errors"](each entry hascode,message, optionaldocumentation_url).{:error, exception}— Req-level transport failure (DNS, TLS, connection refused, etc.).
Pattern-match on specific upstream codes directly:
case Exgit.CloudflareArtifacts.create_token(client, repo: r, ttl: 99_999_999) do
{:error, %Req.Response{body: %{"errors" => [%{"code" => 10103} | _]}}} ->
:ttl_out_of_range
...
end
Summary
Functions
Create a repo. POST /repos.
Mint a repo-scoped token. POST /tokens.
Delete a repo. DELETE /repos/:name. Returns 202 upstream; the
resulting Repo carries only :id.
Revoke a token. DELETE /tokens/:id. The returned Token carries
only :id.
Fork a repo. POST /repos/:name/fork.
Get a repo by name. GET /repos/:name.
Import a public HTTPS git remote. POST /repos/:name/import.
List repos. GET /repos.
List tokens for a repo. GET /repos/:name/tokens.
Build a configured %Req.Request{} for the Cloudflare Artifacts API.
Functions
@spec create_repo( Req.Request.t(), keyword() ) :: {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}
Create a repo. POST /repos.
Required option: :name. Optional: :description,
:default_branch, :read_only. The returned Repo includes a
short-lived inline :token — use create_token/2 for longer TTLs.
@spec create_token( Req.Request.t(), keyword() ) :: {:ok, Exgit.CloudflareArtifacts.Token.t()} | {:error, term()}
Mint a repo-scoped token. POST /tokens.
Required option: :repo. Optional: :scope (`"read" | "write"` or |
| the matching atoms `:read | :write; default"write"),:ttl` |
(seconds, default 86_400; capped at 31_536_000 — code: 10103 if
exceeded). Returns a Token with :plaintext set.
@spec delete_repo(Req.Request.t(), String.t()) :: {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}
Delete a repo. DELETE /repos/:name. Returns 202 upstream; the
resulting Repo carries only :id.
@spec delete_token(Req.Request.t(), String.t()) :: {:ok, Exgit.CloudflareArtifacts.Token.t()} | {:error, term()}
Revoke a token. DELETE /tokens/:id. The returned Token carries
only :id.
@spec fork_repo(Req.Request.t(), String.t(), keyword()) :: {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}
Fork a repo. POST /repos/:name/fork.
Required option: :name (the new repo). Optional: :description,
:read_only, :default_branch_only. The returned Repo adds an
:objects count.
@spec get_repo(Req.Request.t(), String.t()) :: {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}
Get a repo by name. GET /repos/:name.
@spec import_repo(Req.Request.t(), String.t(), keyword()) :: {:ok, Exgit.CloudflareArtifacts.Repo.t()} | {:error, term()}
Import a public HTTPS git remote. POST /repos/:name/import.
name is the destination repo name; :url in the body is the
HTTPS source remote. Optional body fields: :branch, :depth,
:read_only. May 409 while a previous import/fork is still in
progress.
@spec list_repos( Req.Request.t(), keyword() ) :: {:ok, [Exgit.CloudflareArtifacts.Repo.t()], map()} | {:error, term()}
List repos. GET /repos.
Optional: :limit (default 50, max 200), :cursor, :search,
:sort ("created_at" | "updated_at" | "last_push_at" | "name"),
:direction ("asc" | "desc").
@spec list_tokens(Req.Request.t(), String.t(), keyword()) :: {:ok, [Exgit.CloudflareArtifacts.Token.t()], map()} | {:error, term()}
List tokens for a repo. GET /repos/:name/tokens.
Optional: :state (`"active" | "expired" | "revoked" | "all"` or |
| the matching atoms `:active | :expired | :revoked | :all`; default |
"active"), :per_page (default 30, max 100), :page (default
1). Listed tokens have :plaintext set to nil — the API never
re-emits the secret.
@spec new(keyword()) :: Req.Request.t()
Build a configured %Req.Request{} for the Cloudflare Artifacts API.
Required: :account_id, :api_token. Optional: :namespace
(defaults to "default"), :base_url. Any other keys (:plug,
:retry, :receive_timeout, …) are forwarded to Req.new/1.