> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spenza.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Authenticate, browse the catalog, provision an eSIM with a plan, then confirm usage — end to end.

This walks through the fastest path to a working, billable line: authenticate, see what's purchasable, provision a new eSIM with a plan in one call, then confirm usage once it's live. Every request/response shown here is copy-ready — swap in your own credentials and IDs.

## 1. Authenticate

```bash theme={null}
curl -X POST "https://api.spenza.com/api/v1.1/auth/token" \
  -H "Content-Type: application/json" \
  -d '{ "key": "'"$SPENZA_API_KEY"'", "secret": "'"$SPENZA_API_SECRET"'" }'
```

```json theme={null}
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJI…",
    "token_type": "Bearer",
    "expires_in": 3600,
    "expires_at": "2026-07-31T12:00:00.000Z"
  },
  "meta": { "timezone": "UTC" }
}
```

```bash theme={null}
export SPENZA_TOKEN="eyJhbGciOiJI…"
```

## 2. Browse the catalog

Two separate catalogs feed a provision call: SIM products (the physical/eSIM unit itself) and plans (the data/voice/SMS product you attach to it).

```bash theme={null}
curl "https://api.spenza.com/api/v1.1/sim-products?pageSize=10" \
  -H "Authorization: Bearer $SPENZA_TOKEN"
```

```json theme={null}
{
  "success": true,
  "data": [
    {
      "simId": "BIB010A",
      "simName": "AT&T eSIM",
      "description": "eSIM, bulk pricing available",
      "price": 5,
      "currency": "usd",
      "operator": "AT&T",
      "network": "AT&T",
      "isEsim": true,
      "inStock": 500,
      "minOrderQuantity": 1,
      "maxOrderQuantity": 10
    }
  ],
  "meta": { "page": 1, "pageSize": 10, "total": 1, "totalPages": 1 }
}
```

```bash theme={null}
curl "https://api.spenza.com/api/v1.1/plans?pageSize=10" \
  -H "Authorization: Bearer $SPENZA_TOKEN"
```

```json theme={null}
{
  "success": true,
  "data": [
    {
      "productId": "AT0001",
      "productName": "AT&T 5GB",
      "description": "Unlimited talk & text + 5GB data",
      "price": 49.99,
      "currency": "usd",
      "validityDays": 30,
      "data": { "value": 5, "unit": "GB" },
      "voiceMinutes": 1000,
      "smsCount": 500,
      "operator": "AT&T",
      "network": "AT&T",
      "isEsim": false,
      "planType": "FLAT_RATE",
      "recurring": true
    }
  ],
  "meta": { "page": 1, "pageSize": 10, "total": 1, "totalPages": 1 }
}
```

Take note of `simId` (`BIB010A`) and `productId` (`AT0001`) — you'll pass them as `simId`/`planId` in the next step.

## 3. Provision an eSIM with a plan

One call both provisions the eSIM and activates the plan on it. This endpoint is **always asynchronous** — it returns `202` immediately with a `transactionId`, not the finished SIM:

```bash theme={null}
curl -X POST "https://api.spenza.com/api/v1.1/esim" \
  -H "Authorization: Bearer $SPENZA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "imei": "356938035643809",
    "simId": "BIB010A",
    "planId": "AT0001",
    "email": "jane@example.com"
  }'
```

```json theme={null}
{
  "success": true,
  "data": {
    "transactionId": "64f1a2b3c4d5e6f7a8b9c0d1",
    "statusEndpoint": "/api/v3/transactions/64f1a2b3c4d5e6f7a8b9c0d1"
  }
}
```

Poll `statusEndpoint` — no auth header needed, the `transactionId` itself is the credential — until `status` leaves `PENDING`/`PROCESSING`:

```bash theme={null}
curl "https://api.spenza.com/api/v3/transactions/64f1a2b3c4d5e6f7a8b9c0d1"
```

```json theme={null}
{
  "success": true,
  "data": {
    "transactionId": "64f1a2b3c4d5e6f7a8b9c0d1",
    "status": "COMPLETED",
    "message": "Transaction completed successfully",
    "timestamp": "2026-07-31T10:32:00.500Z",
    "result": {
      "mdn": "+12792044584",
      "iccid": "8901260853182965429",
      "qrCode": "https://…/qrcode.png"
    },
    "createdAt": "2026-07-31T10:30:00.000Z",
    "updatedAt": "2026-07-31T10:32:00.000Z"
  }
}
```

Take note of `result.iccid` — that's the identifier for every SIM-scoped call from here on, including the usage check below.

<Note>
  See **[Core Concepts → Async operations](/core-concepts#async-operations)** for the full polling contract, including what a `FAILED` transaction looks like.
</Note>

## 4. Confirm usage

```bash theme={null}
curl "https://api.spenza.com/api/v1.1/sims/8901260853182965429/usage" \
  -H "Authorization: Bearer $SPENZA_TOKEN"
```

```json theme={null}
{
  "success": true,
  "data": {
    "iccid": "8901260853182965429",
    "cycle": { "start": "2026-07-31T00:00:00.000Z", "end": "2026-08-31T00:00:00.000Z" },
    "data": { "used": 0, "allowance": 5, "unit": "GB" },
    "voice": { "usedMinutes": 0, "allowanceMinutes": 1000 },
    "sms": { "used": 0, "allowance": 500 },
    "history": []
  }
}
```

## The same flow in Node.js

```js theme={null}
const BASE = "https://api.spenza.com";

async function pollTransaction(statusEndpoint, { intervalMs = 5000, timeoutMs = 60 * 60 * 1000 } = {}) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const res = await fetch(`${BASE}${statusEndpoint}`);
    const { data } = await res.json();
    if (data.status === "COMPLETED") return data.result;
    if (data.status === "FAILED") throw new Error(`${data.errorCode}: ${data.error}`);
    await new Promise((r) => setTimeout(r, intervalMs));
  }
  throw new Error("Timed out waiting for provisioning to finish.");
}

async function main() {
  const authRes = await fetch(`${BASE}/api/v1.1/auth/token`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      key: process.env.SPENZA_API_KEY,
      secret: process.env.SPENZA_API_SECRET,
    }),
  });
  const authBody = await authRes.json();
  if (!authBody.success) throw new Error(authBody.error.message);
  const headers = {
    Authorization: `Bearer ${authBody.data.access_token}`,
    "Content-Type": "application/json",
  };

  const provisionRes = await fetch(`${BASE}/api/v1.1/esim`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      imei: "356938035643809",
      simId: "BIB010A",
      planId: "AT0001",
      email: "jane@example.com",
    }),
  });
  const provisionBody = await provisionRes.json();
  if (!provisionBody.success) throw new Error(provisionBody.error.message);

  const { iccid } = await pollTransaction(provisionBody.data.statusEndpoint);

  const usageRes = await fetch(`${BASE}/api/v1.1/sims/${iccid}/usage`, { headers });
  const usage = await usageRes.json();
  console.log(usage.data);
}

main().catch(console.error);
```

## What you just did

1. Exchanged credentials for a bearer token.
2. Browsed the SIM-product and plan catalogs your account can purchase.
3. Provisioned a new eSIM with a plan attached in one call, then polled the returned `transactionId` to completion.
4. Checked usage against the new SIM's billing cycle.

## Next steps

* **[Guides](/guides)** for deeper walkthroughs — SIM-only provisioning, port-in, billing, and webhook integration.
* **API Reference** (sidebar) for every field, enum and error on every endpoint.
* **[Errors](/errors)** for how to handle failures in each of these steps.
