> For the complete documentation index, see [llms.txt](https://paykilla.gitbook.io/paykilla-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://paykilla.gitbook.io/paykilla-docs/api-integration/quickstart.md).

# Quickstart

Go from zero to your first crypto payment in a few minutes. This guide walks the shortest happy path: create one invoice, send the customer to pay, and confirm the payment via webhook. For the complete reference, see [Integration](https://paykilla.gitbook.io/paykilla-docs/api-integration/integration).

**Base URL:** `https://account-api.paykilla.com`

***

### 1. Create an API key

In your PayKilla dashboard, go to **Settings → API keys → Generate API keys** and grant the **`INVOICE`** permission.

You receive two values:

* `publicKey` — sent in the `X-API-KEY` header
* `secretKey` — used to sign requests

### 2. Set environment variables

```env
PAYKILLA_V2_API_KEY=your_public_key
PAYKILLA_V2_SECRET_KEY=your_secret_key
```

### 3. Make your first signed request

Every request is authenticated with an **HMAC-SHA256** signature. For a `POST`, you sign `timestamp` + `recvWindow` and pass them (plus the `signature`) in the query string, with your data in the JSON body.

Here is a complete example that creates a $10 invoice. It runs on your **server** — the secret key must never reach the browser:

```typescript
import { createHmac } from "crypto"
import axios from "axios"
import qs from "qs"

const client = axios.create({
  baseURL: "https://account-api.paykilla.com",
  headers: { "X-API-KEY": process.env.PAYKILLA_V2_API_KEY },
})

const sign = (data: string) =>
  createHmac("sha256", process.env.PAYKILLA_V2_SECRET_KEY!).update(data).digest("hex")

async function createInvoice() {
  const timestamp = Date.now()
  const recvWindow = 5000
  const signature = sign(qs.stringify({ timestamp, recvWindow }))

  const { data } = await client.post(
    "/api/v2/invoice",
    {
      type: "FIAT_BASED",
      purpose: "Order 1001",
      currency: "USD",
      totalPrice: "10",
      paymentCurrencies: ["USDTTRC"],
    },
    { params: { timestamp, recvWindow, signature } }
  )

  return data // the created invoice: { id, status: "AWAITING_PAYMENT", ... }
}
```

> `GET` requests are signed slightly differently (all query params are signed together with `timestamp` and `recvWindow`). See [Integration → Authentication](https://paykilla.gitbook.io/paykilla-docs/api-integration/integration#authentication) for the full signing rules.

### 4. Send the customer to pay

Pass the invoice `id` from your server to the page, then redirect the buyer to the hosted checkout, where they choose a coin and complete the transfer:

```typescript
// in the browser, with the invoice id received from your server
window.location.href = `https://gopay.paykilla.com/${invoiceId}`
```

### 5. Check the status

Fetch the invoice any time to read its current status:

```
GET /api/v2/invoice/{id}
```

Typical lifecycle: `AWAITING_PAYMENT` → `PROCESSING` → `SUCCESSFUL`. Other statuses are described in [Integration → Invoice Statuses](https://paykilla.gitbook.io/paykilla-docs/api-integration/integration#invoice-statuses).

### 6. Receive your first webhook

Polling works, but webhooks are the reliable way to learn when an invoice is paid. In the dashboard, set a webhook endpoint URL, attach your API key (it signs the deliveries), and subscribe to `INVOICE_PAID`.

A minimal handler that verifies the signature and marks the order paid:

```typescript
import { createHmac, timingSafeEqual } from "crypto"

const SECRET = process.env.PAYKILLA_V2_SECRET_KEY!
const WEBHOOK_URL = "https://your-domain.com/webhooks/paykilla" // exactly as saved in the dashboard

export async function POST(request: Request) {
  const timestamp = request.headers.get("X-API-TIMESTAMP")
  const recvWindow = parseInt(request.headers.get("X-API-RECV-WINDOW") || "5000", 10)
  const signature = request.headers.get("X-API-SIGN")
  const body = await request.text() // raw body — do not re-serialize

  if (!timestamp || !signature) {
    return new Response("Unauthorized", { status: 401 })
  }

  if (Date.now() - parseInt(timestamp, 10) > recvWindow) {
    return new Response("Request expired", { status: 401 })
  }

  const expected = Buffer.from(createHmac("sha256", SECRET).update(`${timestamp}POST${WEBHOOK_URL}${body}`).digest("hex"), "hex")
  const received = Buffer.from(signature, "hex")
  // timingSafeEqual throws on buffers of different length, so check the length first
  if (received.length !== expected.length || !timingSafeEqual(expected, received)) {
    return new Response("Invalid signature", { status: 401 })
  }

  const event = JSON.parse(body)
  if (event.eventType === "INVOICE_PAID") {
    await markOrderAsPaid(event.data.id)
  }
  return new Response("OK", { status: 200 })
}
```

That's it — you've accepted your first crypto payment.

***

### Next steps

* [Integration](https://paykilla.gitbook.io/paykilla-docs/api-integration/integration) — full invoice API: fees, redirect URLs, items, statuses and TypeScript types
* [Webhooks](https://paykilla.gitbook.io/paykilla-docs/api-integration/webhooks) — the complete event catalogue and signature verification (Node / Python / PHP)
* Move funds — [Withdrawal](https://paykilla.gitbook.io/paykilla-docs/api-integration/withdrawal), [Exchange](https://paykilla.gitbook.io/paykilla-docs/api-integration/exchange)
