---
title: Quickstart
description: Make your first purchase with a test key, then go live.
sidebar:
  order: 1
---

## 1. Get access

Request API access on the [developer page](https://trimcarbon.com/developers/access). Before we approve a request, the account needs two-factor authentication or a passkey. We email you once the account is active.

## 2. Create a test key

On the developer page, create a key in **Test** mode with **Read and buy** access. Copy it: we show it once.

Test keys spend a simulated $1,000 balance. Nothing is charged and no credits are retired, so you can build your integration safely.

## 3. Get a quote

A quote tells you what a purchase would buy and cost, without buying anything:

```bash
curl https://trimcarbon.com/api/v1/quotes \
  -H "Authorization: Bearer $TRIMCARBON_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kg": 100}'
```

## 4. Buy offsets

Send a unique `Idempotency-Key` with every purchase. If the request times out, retry it with the same key: you get the first purchase back instead of buying twice.

```bash
curl https://trimcarbon.com/api/v1/offsets \
  -H "Authorization: Bearer $TRIMCARBON_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"kg": 100, "reference": "fleet-2026-09"}'
```

`POST /offsets`

**Request body** (`application/json`, required)

Exactly one of \`kg\` (whole kg, up to 1,000,000) and \`amount\_cents\`, and an optional \`reference\`.

Request body example, By volume:

```json
{
  "kg": 1000,
  "reference": "fleet-2026-09"
}
```

Request body example, By price:

```json
{
  "amount_cents": 5000
}
```

**Responses**

- `201` — The purchase (or, on a retry, the first one).
- `400` — Bad JSON, or a missing or invalid Idempotency-Key.
- `401` — Missing, unknown or revoked API key.
- `402` — \`insufficient\_balance\`: the balance doesn’t cover the purchase. The error has \`balance\_cents\` and \`required\_cents\`.
- `403` — \`api\_access\_inactive\` (the account is suspended or not approved) or \`insufficient\_permissions\` (a read-only key).
- `409` — \`idempotency\_key\_reused\`: the key was used for a different request.
- `422` — \`validation\_error\` (with \`param\`) or \`amount\_too\_small\`.
- `429` — \`rate\_limited\`: wait for the \`Retry-After\` seconds.

Response example, 201:

```json
{
  "object": "offset",
  "id": "off_8Hq2kP0aZr4mXc1vB7nT3yLw",
  "livemode": true,
  "status": "processing",
  "kg": 0,
  "amount_cents": 0,
  "currency": "usd",
  "rate_millicents_per_kg": 4000,
  "requested": {
    "kg": 0,
    "amount_cents": 0
  },
  "reference": "string",
  "certificate_url": "http://example.com",
  "created": "2026-10-08T14:03:12.000Z",
  "placed": "2026-10-08T14:03:12.000Z"
}
```

## 5. Follow the order

The offset starts as `processing`. Poll it until it is `placed`:

```bash
curl https://trimcarbon.com/api/v1/offsets/off_8Hq2kP0aZr4mXc1vB7nT3yLw \
  -H "Authorization: Bearer $TRIMCARBON_KEY"
```

A test offset is placed after a few seconds. A live one usually takes minutes; then `certificate_url` links to its public certificate.

## 6. Go live

1. Top up your balance on the developer page.
2. Create a **Live** key, and store it as a secret on your server.
3. Swap the test key for the live key. Nothing else changes.

:::warning
Live purchases retire real carbon credits. The balance is prepaid and not refundable; see [Balance and purchases](/docs/balance).
:::
