Skip to main content
The Pierre SDK has one package for each language: @pierre/sdk for TypeScript and JavaScript, pierre-sdk for Python, and github.com/pierrecomputer/sdk/go for Go. This page explains what applies to every call. The reference page of each operation shows the call of that operation in each language.
The earlier Code Storage SDKs, @pierre/storage, pierre-storage, and code-storage-go, have their own docs: Storage SDK. To move code to this SDK, use the pierre-migrate skill.

Install

The TypeScript samples in these docs use import and top-level await. In a Node.js project, set "type": "module" in your package.json, or use .mts files:
package.json

Create a client

The client reads its settings from these environment variables. An empty variable counts as not set.
With a private key, the client signs a 5-minute token for each request, with only the scopes of the operation. With a fixed token, the server checks the scopes of the token. Give one credential: a private key, a token, or a token function. When the environment holds more than one, the client uses PIERRE_TOKEN first, then PIERRE_PRIVATE_KEY, then PIERRE_PRIVATE_KEY_FILE. With no credential, or no organization for the default hosts, the client raises PierreError, or pierre.NewClient returns an error. The Settings page of your organization in the dashboard shows your API host and Git host. Set PIERRE_API_BASE_URL to the API origin and PIERRE_GIT_HOST to the Git host of remote URLs only when they are not the defaults. Give the API origin with no path. The client adds the path of each operation, which starts with /api. TypeScript and Python read PIERRE_PRIVATE_KEY_FILE when they sign the first token. TypeScript needs node:fs for it. Go reads the key at once, also from the file, and returns an error for a key that it cannot read.

Explicit options

An option replaces its variable. A credential in the options replaces every credential in the environment. The comments name the variable of each option. PIERRE_PRIVATE_KEY_FILE has no option.
A client with no private key, such as a browser, gets each token from a function: getToken in TypeScript, get_token in Python, and option.WithGetToken in Go. The client calls it for each request, with the scopes, the repo claim, the lifetime, and the ref policies of that request. Your server signs the token, for example with the token helper.
  • Python: an argument that is None falls back to its variable, then to the default.
  • Go: the function of option.WithGetToken gets the context of the attempt and an option.TokenRequest.
Authentication explains keys, scopes, and token claims.

Call an operation

The method path is the catalog ID of the operation. The params and the results use the wire names of the HTTP API, such as repo_name and target_branch.
  • TypeScript: repos.branches.create is pierre.repos.branches.create(). Each method takes one params object, and the request options as its second argument.
  • Python: each segment is in snake_case, so repos.gitCredentials.create is pierre.repos.git_credentials.create(). Each method takes keyword arguments. An optional argument has the default NOT_GIVEN, and the client does not send it. None sends null in a body field, and raises PierreError in a query or header argument.
  • Go: the path is in Go names, client.Repos.Branches.Create, with the params struct pierre.ReposBranchesCreateParams. Each method takes a context.Context, the params, and request options. The JSON tags are the wire names. A required field has its plain type, and an optional field is a pierre.Opt[T]. A params struct has no deprecated field of the API. A nested request type, such as pierre.BaseRepo, and a response type keep their deprecated fields, each with a Deprecated: comment.
The reference page of each operation shows its call, for example repos.branches.create.

Options

Client options

The defaults are a timeout of 60 seconds, 2 retries, no headers, the standard HTTP client, and no ref policies. The HTTP client is fetch in TypeScript, an httpx.Client in Python, and an *http.Client in Go. The timeout applies to each attempt. The headers can replace the Pierre-Client header, for example pierre-sdk-typescript/<version>. The options of a client hold its settings, with no credential: pierre.options in TypeScript and Python, and client.Options() in Go. withOptions (with_options in Python, WithOptions in Go) returns a copy of the client with changed options. The copy keeps the credential and each option that you do not change. Ref policies shows an example.
  • Python: a client keeps one httpx client. with Pierre() as pierre: closes the httpx client that the SDK made at the end of the block. An http_client that you give stays open. For a client that lives as long as the process, create it one time.
  • Go: one option.RequestOption serves a client and one request, and an option for the wrong use returns an error. In client.WithOptions, option.WithHeader adds a header to the default headers.

Request options

A request option holds a setting for one call, and replaces the client option for that call. It goes in the second argument in TypeScript, in the keyword arguments in Python, and in the last arguments in Go.
  • To stop a call, use the signal in TypeScript, cancel the asyncio task in Python, or cancel the context.Context in Go.
  • Response headers shows the response option: withResponse() in TypeScript, pierre.with_raw_response in Python, and option.WithResponseInto in Go.

Ref policies

The ref policies limit the refs that a write can change. The client puts the rules in the refs claim of each request token. The first rule whose pattern matches a ref applies.
  • A pattern is a full ref name (refs/heads/main), a branch name (main), a prefix that ends with /*, or *. The ops are no-push, no-force-push, and verify-sig.
  • A request option replaces the rules of the client for that call. An empty list removes them: [], or option.WithRefPolicies() with no rule.
  • The token function receives the rules.
  • A client with a fixed token cannot use ref policies, because the client cannot change the claims of that token.
  • The token and remote URL helpers use the rules of the client when the call gives none, so a confined client stays confined through Git.
Ref Policies explains each op.

Responses

A call gives one of three kinds of result: a JSON body, no body, or bytes.
The results use the wire names of the response. repos.files.metadata.get has no body, and repos.files.get and repos.archive.get give bytes. In TypeScript, each method returns an APIPromise, and the request starts when you call the method.

Response headers

The response of a call gives its headers and its request ID.
  • TypeScript and Python: data is the result of the call, and response is the response of the last attempt. headers holds the response headers that the reference page declares, with the wire form of each name, such as x_cache, etag, and x_blob_sha. request_id is the X-Request-Id header, or none. In a browser, the client can read only the headers that the API exposes through CORS.
  • Go: option.WithResponseInto stores the *http.Response of the last attempt, with every header.

Byte responses

A byte response returns when its headers arrive. You read the body, so the timeout ends with the headers, and the client does not retry the body. In Python and Go, close the stream when you are done, as Responses shows. In TypeScript, asResponse() gives the platform Response of a JSON operation when its headers arrive, and does not read the body. Use it to read a large body yourself. After await or withResponse(), the client has already read the body.
TypeScript

Lists and pagination

A list method returns the first page. A loop on the result gives the items of every page:
A page has the items in data, with has_more, next_cursor, body, and response (Data, HasMore, NextCursor, Body, and Response in Go). body is the full response body, with the fields beside the items. The page iterator gives this page, then each next page:
To move one page at a time, check for a next page with hasNextPage(), then get it with getNextPage(): has_next_page() and get_next_page() in Python, HasNextPage() and GetNextPage(ctx) in Go. The next page sends the params and the request options of the first call. To store a page, serialize its body, the response that the API sent. JSON.stringify(page) and json.Marshal(page) give the body. In TypeScript, withResponse() and asResponse() on a list give the response of the first page.

Errors

Every error of the SDK extends PierreError. Import the classes from @pierre/sdk or pierre. In Go, each error is a pointer type, such as *pierre.NotFoundError, that unwraps to a *pierre.PierreError, and you branch with errors.As.
  • APIError: the API sent an error response. It has status, code, title, detail, request_id, headers, body, and doc_url.
  • The APIError of each status family. Another status, such as 412, gives a plain APIError.
    • BadRequestError: 400
    • AuthenticationError: 401
    • PermissionDeniedError: 403
    • NotFoundError: 404
    • ConflictError: 409
    • RateLimitError: 429
    • InternalServerError: 5xx
  • MergeConflictError: a ConflictError for a merge conflict, with conflict_type, conflict_paths, and merge_base_sha.
  • RefGuardError: a ConflictError with the code ref_guard_failed, with guard and current_sha.
  • RefUpdateError: an APIError for a Git write whose ref update failed, with result and commit.
  • APIConnectionError: the request got no response.
  • APITimeoutError: an APIConnectionError. The attempt got no response before its timeout.
  • WebhookVerificationError: the webhook helper rejected a delivery. reason says why.
Branch on the class and on code, not on the message. Error codes lists the codes.
  • A failed ref guard raises RefGuardError. guard names the request field of the guard, for example expected_target_sha. current_sha is the current commit of the ref. Send it as the guard of the next attempt.
  • Another failed ref update raises RefUpdateError. Its result has status, which gives the reason, for example conflict, with no cast. result is a RefUpdateResult, or a NotesWriteResult for a notes write. In Go, Result is always a RefUpdateResult, and a notes write fills only Success, Status, and Message. code can be empty on a RefUpdateError.
  • A NotFoundError has the code repository_not_found when the repository does not exist. A missing object in the repository, such as a file or a note, has the code not_found.
  • Go: check *pierre.RefGuardError before *pierre.ConflictError, because it unwraps to one. A *pierre.RefUpdateError unwraps to the *pierre.APIError, not to a *pierre.ConflictError. The end of the context of a call returns the cause of the context, and an error of your own function, such as Open of a pierre.CommitFile, reaches you as it is.

Retries and timeouts

The client retries a connection error, a timeout, 408, 429, 5xx, and 409 with the code repository_thawing or sync_in_progress. It retries only GET, HEAD, PUT, and DELETE requests, so a write such as repos.commits.create never retries. It waits for Retry-After when the value is 60 seconds or less. Else it backs off from 0.5 seconds, up to 8 seconds. Each attempt has its own timeout: 60 seconds by default. The timeout covers the token, the upload, the response headers, and a JSON body. Give a larger timeout for a large commit. When the last attempt times out, the call raises APITimeoutError. A retried DELETE can get a 404 when the first attempt worked and its response was lost. To turn off the retries of a call, set its retries to 0: maxRetries: 0, max_retries=0, or option.WithMaxRetries(0). In Python, Pierre cannot stop a blocking get_token, so the attempt fails when the token arrives after the deadline.

Python async code

AsyncPierre takes the same arguments as Pierre, and each method is async. Its get_token can be async, and its http_client is an httpx.AsyncClient.
Python
  • A method starts its request when you await it, as a coroutine does.
  • A list method returns an AsyncPaginator. await gives the first AsyncPage, and async for gives the items of every page. get_next_page() and iter_pages() of an AsyncPage are async.
  • async with closes the httpx client that the SDK made.
  • To stop a call, cancel its task.
  • With AsyncPierre, await the result of remote_url. create_token stays synchronous.

Go optional fields

An optional field, or a field that can be null, is a pierre.Opt[T]:
  • The zero value is omitted. The request does not send it, so the server keeps the stored value.
  • pierre.String, pierre.Int (an int64), pierre.Float, pierre.Bool, and pierre.Value give a value. pierre.Value takes any type, such as a struct.
  • pierre.Null[T]() gives null, which resets the stored value.
Go
A response field that is optional or can be null is an Opt too. Get gives the value and whether the response has one, and IsNull reports null. An enum is a string type with a constant for each known value, such as pierre.RefUpdateResultStatusConflict. A response can hold a value that this SDK version does not list, so give each switch on an enum a default case. In every language, an enum is open.

Go contexts

Each method takes a context.Context as its first argument. The context stops the call at any time, also during the wait between two attempts. The call then returns the cause of the context. option.WithTimeout limits each attempt, and a context limits the whole call, with its retries.
Go

Local helpers

These helpers make no call to the API:
  • TypeScript: no subpath loads another, so a browser or edge bundle holds only what it imports. @pierre/sdk/core exports PierreCore, the client with no namespaces, and @pierre/sdk/repos exports ReposNamespace, built with new ReposNamespace(new PierreCore()).
  • Python: import pierre does not load these modules, and a variable named pierre hides the module. Import them with from pierre.<module> import ....

Tokens

The token helper signs a token with the private key of a client, or with an organization and a private key. A client with a fixed token or a token function cannot sign.
  • scopes names at least one scope. SCOPES (auth.Scopes in Go) lists the scopes of the API.
  • repo sets the repo claim. Leave it out for a token whose scopes are all organization-wide.
  • ttl is the lifetime, from 1 second to 1 hour. Default: 15 minutes.
  • ref_policies sets the refs claim. Default: the ref policies of the client.
Authentication lists the scopes and the claims.

Git remote URLs

The remote URL helper builds https://t:<token>@{gitHost}/{repo_name}.git for git clone, git fetch, and git push.
  • The token has git:read and git:write, and lives 15 minutes. The scopes and the TTL change them, up to 1 hour.
  • The kind ephemeral gives the +ephemeral remote, and the kind import gives the +import remote: kind: 'ephemeral', kind="ephemeral", or Kind: git.KindEphemeral.
  • It works with a private key or a token function. A client with a fixed token cannot set the claims of its token, so the helper raises PierreError.
Git copies the remote URL and its token into .git/config, so keep the lifetime short. For longer work, use the credential helper of the CLI, pierre auth setup-git. See Git Operations, Ephemeral Branches, and Imports.

Webhooks

The webhook helper checks the X-Pierre-Signature header of a delivery and returns the typed event. It raises WebhookVerificationError when the check fails.
  • The payload is the raw request body. Do not pass parsed JSON.
  • The headers are the request headers: a Fetch Headers or a record in TypeScript, a mapping in Python, and an http.Header in Go.
  • The tolerance is the largest age of a delivery. Default: 5 minutes.
  • An event type that this SDK version does not know gives an unknown event, with the type that Pierre sent and the parsed payload: type: 'unknown' with event_type and body in TypeScript and Python, and a *webhooks.UnknownEvent with Type and Body in Go.
Webhooks describes the events and their payloads.