Skip to main content

SDK

Install @easel-sh/sdk and call the Easel API from Node.js, Bun, Deno, or edge runtimes.

@easel-sh/sdk is the TypeScript client for the Easel API. Each method is a plain async function over fetch, so it runs in a route handler, a queue worker, a CLI, or an edge function alike.

Install

npm install @easel-sh/sdk

The main entry runs on Node.js 20 or later, Bun, Deno, and edge runtimes; it uses only fetch and Web Crypto. @easel-sh/sdk/node adds helpers that read from disk, and needs Node.js.

Create a client

import { createClient } from "@easel-sh/sdk";

const easel = createClient({ token: process.env.EASEL_TOKEN! });
OptionDescription
tokenRequired. A workspace or scoped token
baseUrlDefaults to https://app.easel.sh, or EASEL_API_URL when it's set
maxAttemptsAttempts for rate-limited, 5xx, and network failures. Default 4
fetchA custom fetch, for tests or runtimes without a global one
userAgentSent as User-Agent. Defaults to easel-sdk/<version>

Keep the client on your server. Never send the token to a browser or into a sandbox.

Retries and errors

The client retries 429 responses, and 5xx and network failures for reads and for requests with an Idempotency-Key, with backoff. Every creating method sends a new idempotency key unless you pass idempotencyKey, so a retry never creates a second resource. Pass your own key, such as your publish's ID, to make a whole publish safe to retry.

A non-2xx response throws EaselApiError:

import { EaselApiError } from "@easel-sh/sdk";

try {
  await easel.domains.add(projectId, { name: "trips.example.com" });
} catch (error) {
  if (error instanceof EaselApiError && error.code === "domain_in_use") {
    showRecords(error.records);
  } else {
    throw error;
  }
}

EaselApiError has status, code, records, and the API's message. See Errors for the codes.

Projects and environment variables

MethodDescription
projects.ensure(name, settings?)The project named name, created with settings if it doesn't exist. Safe on every publish
projects.list(), get(project)Read projects
projects.create(input), update(project, changes), delete(project)Manage a project
env.upsert(project, vars)Create variables, replacing any with the same key and branch
env.list(project, { decrypt? })List variables, with values for non-sensitive ones when decrypt is true
env.create, env.update, env.deleteManage single variables

project is a project's ID or slug everywhere.

Deploy

MethodDescription
deployments.create({ project, target?, meta?, archive | files, prebuilt?, idempotencyKey? })Create a deployment; returns once it's queued
deployments.createUpload({ project, target?, meta?, expiresIn? })A single-use upload URL for a sandbox
deployments.getUpload(uploadId)Whether an upload URL was used, and its deploymentId
deployments.wait(id, { onStatus?, timeoutMs?, intervalMs? })Poll until the deployment reaches a final status
deployments.get(id), list({ project, … })Read deployments
deployments.logs(id), runtimeLogs(id, { since?, limit? })The build log, and the requests it served
deployments.cancel(id)Cancel a queued or building deployment
deployments.promote({ project, deploymentId })Serve a deployment in production. Promoting a preview returns a new deployment with rebuilt: true
deployments.rollback({ project, deploymentId })Serve an earlier production deployment again

deployments.create() takes exactly one source:

  • archive: a gzip tarball, as bytes, a Blob, or a ReadableStream, such as tar czf - output. Sent in one request.
  • files: [{ file, data, mode? }], with data as a string or bytes. A set up to 2 MiB (INLINE_FILES_MAX_BYTES) goes inline in one request. Larger sets upload only the files Easel doesn't already have, then create the deployment.

wait() calls onStatus with the deployment each time its status changes, and throws DeploymentWaitTimeoutError after timeoutMs, 10 minutes by default.

sandboxUploadScript(uploadUrl, { cwd?, exclude? }) returns the shell command that uploads a sandbox's working directory to an upload URL. See Deploy source.

Domains, usage, webhooks, and tokens

MethodDescription
domains.add(project, { name }), list, get, verify, removeA project's domains
wildcardDomains.add({ domain }), list, get, verify, removeYour workspace's wildcard domains
domainTarget.set(host), get, verify, removeYour branded CNAME target
usage.get({ from?, to?, projectId? })Usage per project
webhooks.create({ url, events, projectIds? }), list, deleteManage webhooks
webhooks.verify(rawBody, headers, secret)Check a delivery's signature and return its event
tokens.create({ name, permissions, projectIds?, expiresIn }), list, revokeMint and revoke scoped tokens
whoami()The token's user and workspace
request(method, path, options)Call an endpoint the client doesn't wrap

verifyWebhook() and signWebhook() are also exported on their own, for a webhook receiver that doesn't create a client. See Webhooks.

Deploy a folder from Node.js

@easel-sh/sdk/node deploys a directory on disk. It skips the same files as easel deploy, including the patterns in the folder's .easelignore, packs the rest into one tarball, and sends it:

import { createClient } from "@easel-sh/sdk";
import { deployDirectory } from "@easel-sh/sdk/node";

const easel = createClient({ token: process.env.EASEL_TOKEN! });
const deployment = await deployDirectory(easel, {
  dir: "./out",
  project: "trip-planner",
  target: "production",
});

collectDeployFiles() and createSourceArchiveTgz() are the two steps on their own.

Types

Every request and response type is exported, such as Deployment, ProjectDomain, WebhookEvent, and PermissionMap, along with the lists WEBHOOK_EVENT_TYPES and PERMISSIONS.

Next steps