Create an Invoice

Create a USDC invoice for agent payment
View as Markdown

Create an invoice

1import { Gwop } from "@gwop/sdk";
2
3const gwop = new Gwop({
4 merchantApiKey: process.env.GWOP_MERCHANT_API_KEY,
5});
6
7const { result: invoice } = await gwop.invoices.create({
8 idempotencyKey: crypto.randomUUID(),
9 body: {
10 amountUsdc: 5_000_000, // $5.00 USDC (6 decimals)
11 description: "Starter plan — 300 credits",
12 metadata: { planId: "starter" },
13 },
14});
15
16console.log(invoice.publicInvoiceId); // inv_7dbeeaad...
17console.log(invoice.status); // "OPEN"
18console.log(invoice.agentProtocol); // Machine-readable payment instructions

Response

1{
2 "id": "ba7bc94a-5468-42f4-9f04-2502e50c7501",
3 "publicInvoiceId": "inv_7dbeeaad8ebf4f5298c380c90e4b3576",
4 "merchantId": "9e9c9ba5-3bab-4172-8a79-336f9ca0f163",
5 "amountUsdc": 1025000,
6 "currency": "USDC",
7 "status": "OPEN",
8 "description": "SDK test invoice",
9 "metadata": {
10 "test": "true",
11 "fee_usdc": 25000,
12 "base_amount_usdc": 1000000,
13 "fee_basis_points": 250,
14 "total_amount_usdc": 1025000
15 },
16 "metadataPublic": false,
17 "createdAt": "2026-03-25T01:42:45.287Z",
18 "expiresAt": "2026-03-25T01:57:45.232Z",
19 "agentProtocol": { "..." : "..." }
20}

Parameters

FieldTypeRequiredDefaultDescription
amountUsdcnumberYesAmount in atomic USDC (6 decimals). 1_000_000 = $1.00
descriptionstringNoHuman-readable invoice description
metadataobjectNoArbitrary key-value pairs stored with the invoice
metadataPublicbooleanNofalseWhen true, metadata is visible to the payer via the public invoice endpoint
expiresInSecondsnumberNo900Invoice TTL in seconds. Minimum 60, default 15 minutes

The idempotencyKey is sent as a header. Always use crypto.randomUUID() for new invoices.

Two IDs

Every invoice has two identifiers:

FieldFormatWho uses itExample
idUUID v4Merchant (your backend)ba7bc94a-5468-42f4-9f04-2502e50c7501
publicInvoiceIdinv_*Payer / agentinv_7dbeeaad8ebf4f5298c380c90e4b3576

Your backend stores id for cancel and internal lookups. You hand publicInvoiceId to the agent.

Fees

amountUsdc in the response includes Gwop’s processing fee. If you send 1_000_000 (1.00),theresponsereturns1025000(1.00), the response returns `1_025_000` (1.025) at the current 2.5% fee rate. The fee breakdown is injected into metadata:

1{
2 "fee_usdc": 25000,
3 "base_amount_usdc": 1000000,
4 "fee_basis_points": 250,
5 "total_amount_usdc": 1025000
6}

Build your pricing around the base amount you send, not the total in the response.

Agent Protocol

The agentProtocol field contains machine-readable instructions for AI agents to complete payment. You don’t need to parse it — hand it to the agent as-is.

1{
2 "version": "1.0",
3 "flow": "invoice_checkout",
4 "state": "invoice_created",
5 "goal": "Reach invoice payment completion deterministically.",
6 "steps": [
7 "Fetch invoice details to discover available payment_methods.",
8 "Choose a method+network option from payment_methods and execute its contract."
9 ],
10 "nextAction": {
11 "method": "GET",
12 "path": "/v1/invoices/inv_7dbeeaad...",
13 "expectStatus": [200, 404]
14 },
15 "completionCheck": {
16 "method": "GET",
17 "path": "/v1/invoices/inv_7dbeeaad...",
18 "field": "status",
19 "successValue": "PAID"
20 }
21}

The protocol tells the agent:

  1. What to do next (nextAction) — fetch the invoice to see payment methods
  2. How to know it’s done (completionCheck) — poll until status === "PAID"

Idempotency

Passing the same idempotencyKey returns the original response without creating a duplicate invoice. This makes retries safe:

1const key = crypto.randomUUID(); // generate once, retry with same key
2
3// First call creates the invoice
4const { result: first } = await gwop.invoices.create({
5 idempotencyKey: key,
6 body: { amountUsdc: 5_000_000 },
7});
8
9// Retry with same key returns the same invoice
10const { result: second } = await gwop.invoices.create({
11 idempotencyKey: key,
12 body: { amountUsdc: 5_000_000 },
13});
14
15first.id === second.id; // true

Always generate the idempotency key before the try/catch block so retries use the same key.

Custom Expiry

Invoices expire after 15 minutes by default. Set expiresInSeconds to customize:

1const { result } = await gwop.invoices.create({
2 idempotencyKey: crypto.randomUUID(),
3 body: {
4 amountUsdc: 1_000_000,
5 expiresInSeconds: 60, // 1 minute
6 },
7});
8
9// expiresAt will be ~60 seconds after createdAt

Public Metadata

By default, metadata is only visible to the merchant. Set metadataPublic: true to make it visible to the payer through the public invoice endpoint:

1const { result } = await gwop.invoices.create({
2 idempotencyKey: crypto.randomUUID(),
3 body: {
4 amountUsdc: 5_000_000,
5 description: "Starter plan",
6 metadata: { planName: "Starter", credits: 300 },
7 metadataPublic: true, // agent can see this
8 },
9});

This is useful when the agent needs context about what it’s paying for.

Next step