# Quickstart

This walks from a fresh API key to a working integration. Every example is
copy-pasteable; replace the key and ids with your own.

## 1. Get a key

In the Fynex dashboard, open **Integration** in the left-hand menu and issue a
**secret** key from the API keys card.

- `sk_test_…` — authenticates while your account is in demo mode.
- `sk_live_…` — authenticates once your account is live.

An account is in exactly one of those modes, so exactly one of the two key
environments works at any time.

Keys are stored only as hashes, so a key is shown once, at creation. If you
lose it, regenerate — there is no recovery. Keep keys server-side: they carry
full read access to your billing data.

## 2. Set your base URL

Every endpoint sits under the path `/billing-api/v1`. The host depends on the
environment you were onboarded to, so the examples below use a variable:

```bash
export FYNEX_API_BASE="https://staging-api.fynex.ai/billing-api/v1"
export FYNEX_API_KEY="sk_test_4f6f..."
```

| Environment | Host |
| --- | --- |
| Staging | `https://staging-api.fynex.ai` |
| Production | Confirm your production host with Fynex before going live — it is assigned per deployment, and a `sk_live_` key is only accepted by the environment that issued it. |

You are reading these docs on the same host the API runs on, so
`/billing-api/v1/openapi.json` and `/billing-api/v1/docs` are always
reachable relative to wherever this page is served.

## 3. Make your first call

`GET /contracts` is the safest first request — it needs no ids and tells you
whether your key, environment, and network path all work:

```bash
curl -s $FYNEX_API_BASE/contracts \
  -H "Authorization: Bearer $FYNEX_API_KEY"
```

```json
{
  "contracts": [
    {
      "contractId": 42,
      "contractNumber": "UK2607AA",
      "version": 1,
      "sellerCustomerId": 7,
      "currency": "EUR",
      "status": "active",
      "startDate": "2026-01-01",
      "customerName": "Ada Lovelace",
      "customerCompanyName": "Harbour Group BV"
    }
  ]
}
```

`contractId` is the key to every per-contract endpoint that follows.

If instead you get:

- `401` — the header is missing or the key is wrong. The format is
  `Authorization: Bearer sk_live_…`; a bare key without `Bearer ` fails.
- `403` — the key is valid but the seller account is not active. Check the
  account status in the dashboard.
- an empty `contracts` array — the key works, but this account has no
  contracts yet.

## 4. List invoices

```bash
curl -s "$FYNEX_API_BASE/invoices?limit=5" \
  -H "Authorization: Bearer $FYNEX_API_KEY"
```

```json
{
  "invoices": [
    {
      "id": 4180,
      "contractId": 42,
      "sellerCustomerId": 7,
      "origin": "usage",
      "invoiceNumber": "UK2607AA-2608AAB",
      "jurisdiction": "UK",
      "invoiceType": "standard",
      "status": "sent",
      "currency": "EUR",
      "issueDate": "2026-08-01T00:00:00Z",
      "dueDate": "2026-08-15T00:00:00Z",
      "subtotalMinor": 10000,
      "taxTotalMinor": 2000,
      "grandTotalMinor": 12000,
      "creditAppliedMinor": 0,
      "collectibleMinor": 12000,
      "creditSettledMinor": 0,
      "reverseCharge": false,
      "paymentLinkId": 991
    }
  ],
  "hasMore": true,
  "nextCursor": 4180,
  "nextBeforeId": 4180
}
```

Two things to internalize right now, because they cause most integration
bugs:

- **Amounts are integers in minor units.** `12000` in EUR is €120.00. Never
  parse them as floats.
- **`grandTotalMinor` is the document total, `collectibleMinor` is what
  collection asks the customer for.** They differ when stored credit funded
  part of the invoice. Neither drops to zero once the invoice is paid — to
  decide "does this customer still owe us money", read `status`.
- **`creditSettledMinor` is not a payment total.** It counts only stored
  credit drawn down, so a card-paid invoice reports `0` there.

## 5. Get one invoice with its lines

```bash
curl -s $FYNEX_API_BASE/invoices/4180 \
  -H "Authorization: Bearer $FYNEX_API_KEY"
```

```json
{
  "invoice": { "id": 4180, "invoiceNumber": "UK2607AA-2608AAB", "...": "..." },
  "lines": [
    {
      "position": 1,
      "description": "API calls over allowance",
      "quantity": "250",
      "unitPriceMinor": 40,
      "discountMinor": 0,
      "netAmountMinor": 10000,
      "taxCategory": "standard",
      "taxRate": "20",
      "taxAmountMinor": 2000,
      "sourceRef": "usage:api_calls:2026-07"
    }
  ]
}
```

`quantity` and `taxRate` are decimal **strings** — they carry fractional
precision that a JSON number would round. Feed them to a decimal type, not a
float.

## 6. Download the PDF

```bash
curl -s $FYNEX_API_BASE/invoices/4180/pdf \
  -H "Authorization: Bearer $FYNEX_API_KEY" \
  -o invoice-UK2607AA-2608AAB.pdf
```

The response is `application/pdf` bytes with a `Content-Disposition`
filename. It renders from the document's frozen payload, so a PDF fetched
today and one fetched next year are identical.

## Next

- **Workflows** shows the end-to-end recipes: collecting on an invoice,
  reconciling a month, watching usage against limits.
- **Pagination & Amounts** covers the paging loop you will need past 20 rows.
- **Errors** covers the status contract, rate limits, and retry policy.
