StartGet started

Quickstart

From an API key to your first read and your first write in five minutes — with curl, the SDK or the CLI.

Every Era module — sales, shop, bookings, finance, inbox and the rest — is one API. Every model you can see in the app is a resource you can read, and every button is an action you can call. This page takes you from nothing to a working call.

Create an API key

In Era open Settings → Advanced → API keys (/settings/api-keys) and click New key. Name it after what will use it ("Accounting sync"), choose which modules it reaches, and copy the key — it starts with era_ and is shown once.

The key works as you: it can never do more than you can in that workspace, and if your role shrinks, the key shrinks with it. Keep it on a server; never put it in a browser or a Git repository.

Terminal
export ERA_API_KEY="era_…"

Check who you are

curl https://app.getera.io/api/v1/me \
  -H "Authorization: Bearer $ERA_API_KEY"
Response
{
  "user": { "id": "…", "name": "Ada Lovelace" },
  "workspace": { "id": "…", "organization_id": "…" },
  "credential": { "kind": "key", "id": "…", "client_id": null, "scopes": ["*"] },
  "modules": { "crm": ["read", "write"], "retail": ["read", "write", "manage"] }
}

modules is exactly what this key may do right now: each module, and the capabilities it holds there.

Read something

Every resource lists the same way. Filters are plain query parameters:

curl "https://app.getera.io/api/v1/orders?status=unfulfilled&limit=20" \
  -H "Authorization: Bearer $ERA_API_KEY"
Response
{ "data": [ { "id": "…", "number": 1042, "status": "unfulfilled", "total_amount": 125000, "…": "…" } ], "next_cursor": "…" }

Pass next_cursor back as ?cursor= for the next page. GET /api/v1 lists every resource your key can read.

Do something

Writes are actions. They run the same checks as the button in the app — permission, validation, preconditions — and leave the same trail in the activity feed.

curl -X POST https://app.getera.io/api/v1/customers \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name": "Ada Lovelace", "email": "[email protected]"}'
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const customer = await era.customers.create({ name: "Ada Lovelace", email: "[email protected]" })
const { id } = customer.result as { id: string }
await era.leads.captureLead({ customerId: id, message: "Wants the yearly plan" })
era customers create --name "Ada Lovelace" --email [email protected]
era crm capture_lead --customer-id CUSTOMER_ID --message "Wants the yearly plan"
Response
{ "ok": true, "result": { "id": "…" }, "events": ["customers.created"] }

events are what the action published — what your workflows and webhooks react to.

When something is refused

Errors always have one shape:

{ "error": { "code": "customer_not_found", "message": "The person must already exist as a customer.", "status": 409, "request_id": "req_…" } }
StatusMeaning
401No credential, or it is wrong, expired or revoked.
403The credential does not hold the capability the action needs (e.g. retail:manage).
404No such record — or it, or its whole module, is outside what this credential may reach. Era does not say which.
409A precondition failed; code is the precondition's code (each action's page lists them). Also idempotency_in_progress.
422Invalid input; problems lists each field and what is wrong.
429Too many requests; wait for Retry-After.

Send an Idempotency-Key with every write you might retry: the same key replays the first answer for 24 hours (Idempotency-Replayed: true) instead of doing the work twice.

Next

Was this page helpful?