Skip to content

gwop-io/gwop-node

Repository files navigation

gwop

Infrastructure for agent-native commerce. Turn AI agents into customers.

npm CI License: MIT TypeScript

Gwop gives merchants the missing commerce layer for selling to AI agents. Authenticate agents by wallet, sell subscriptions and credits with USDC, and manage customers — all headless, all API-first.

What merchants build

Gwop is designed for headless, agent-native stores — no browser, no UI, no human in the loop:

  • Credit-based APIs — Sell prepaid credits, enforce usage limits, track consumption per agent
  • Subscription services — Plans with tiered access, daily caps, and model restrictions
  • One-time purchases — Reports, datasets, API packages, or any digital good
  • Agent marketplaces — Multi-tenant platforms where agents discover, buy, and use services

Live example: AgentRouter (skill.md) — a headless LLM inference store built entirely on Gwop.

Why not raw x402?

Raw x402 gives you stateless per-request payments. Gwop gives you customers:

Raw x402 With Gwop
Anonymous wallet hits endpoint Authenticated agent with identity and session
Pay per request, every request Authenticate once, charge upfront, usage flows smoothly
No concept of who paid before Account history, entitlements, plan enforcement
Merchant builds auth + billing One SDK: auth, checkout, webhooks, treasury

Get a merchant account

You need a Gwop merchant account to get API keys, merchant wallets, and webhook secrets.

Install

npm install @gwop/sdk
# or
yarn add @gwop/sdk
# or
pnpm add @gwop/sdk
# or
bun add @gwop/sdk

ESM only. For CommonJS projects, use await import("@gwop/sdk").

Quick start

Add your credentials to .env — find these in your merchant dashboard under Settings:

GWOP_MERCHANT_API_KEY=sk_m_...   # Settings → API Keys → + Create
GWOP_WEBHOOK_SECRET=whsec_...    # Settings → Webhook Configuration → Secret
import { Gwop } from "@gwop/sdk";

const gwop = new Gwop(); // reads GWOP_MERCHANT_API_KEY from env

// Create an invoice
const { result: invoice } = await gwop.invoices.create({
  idempotencyKey: crypto.randomUUID(),
  body: {
    amountUsdc: 5_000_000, // $5.00 USDC (6 decimals)
    description: "Starter plan — 300 credits",
    metadata: { planId: "starter" },
  },
});

// Hand the payment URL to the agent
console.log(invoice.publicInvoiceId); // inv_7dbeeaad8ebf...
console.log(invoice.agentProtocol);   // Machine-readable payment instructions

// Check payment status
const { result: status } = await gwop.invoices.get({
  id: invoice.publicInvoiceId,
});
console.log(status.status); // "OPEN" → "PAYING" → "PAID"

Authenticate agents

Identify agents by wallet using a x402 authentication challenge:

// 1. Create an auth challenge ($0.001 USDC dust payment)
const { result: intent } = await gwop.authIntents.create({
  idempotencyKey: crypto.randomUUID(),
});

// 2. Agent pays → exchange for a JWT
const { result: token } = await gwop.authIntents.exchange({
  authIntentId: intent.authIntentId,
  idempotencyKey: crypto.randomUUID(),
});

console.log(token.accessToken);          // RS256-signed JWT
console.log(token.principal.sub);        // "base:0x742d..." or "solana:7sSi..."
console.log(token.account.isNewAccount); // true on first auth

Your backend calls these endpoints with sk_m_* keys. Agents never talk to Gwop directly — only your backend does. See full auth docs for JWT verification, session management, and JWKS.

Webhooks

const gwop = new Gwop(); // reads GWOP_WEBHOOK_SECRET from env

const event = await gwop.validateWebhook({
  request: {
    body: rawBody,
    headers: {
      "x-gwop-signature": req.headers["x-gwop-signature"],
      "x-gwop-event-id": req.headers["x-gwop-event-id"],
      "x-gwop-event-type": req.headers["x-gwop-event-type"],
      "content-type": "application/json",
    },
    url: `https://${req.headers.host}${req.originalUrl}`,
    method: "POST",
  },
});

switch (event.body.eventType) {
  case "invoice.paid":    // funds received
  case "invoice.expired": // invoice TTL reached
  case "invoice.canceled": // merchant canceled
}

Use the raw body for verification. Re-stringified JSON will break the HMAC signature. Works in Node.js, Deno, Bun, and edge runtimes.

API reference

Invoices

Method Description
gwop.invoices.create() Create an invoice for agent payment
gwop.invoices.list() List invoices with pagination and status filter
gwop.invoices.get() Get the public invoice view (takes publicInvoiceId)
gwop.invoices.cancel() Cancel an open invoice (takes merchant UUID id)

Auth

Method Description
gwop.authIntents.create() Create a wallet auth challenge
gwop.authIntents.exchange() Exchange settled intent for JWT (402 if unpaid)
gwop.authSessions.get() Get session status
gwop.authSessions.revoke() Revoke a session (logout)
gwop.auth.getJwks() Fetch JWKS for local JWT verification

Webhooks

Method Description
gwop.validateWebhook() Verify HMAC signature + parse typed event

Errors

All errors use UPPER_SNAKE_CASE codes. The SDK exports constants and a helper so you never need to string-match:

import { ErrorCode, isGwopError } from "@gwop/sdk/errors";

try {
  await gwop.invoices.get({ id: "inv_missing" });
} catch (err) {
  if (isGwopError(err, ErrorCode.InvoiceNotFound)) {
    // handle missing invoice
  }

  if (isGwopError(err, ErrorCode.RateLimited)) {
    // back off and retry
  }

  if (isGwopError(err)) {
    // any SDK HTTP error — access statusCode, headers, body
    console.log(err.statusCode);
  }
}

For manual access without the helper:

import { ErrorCode, ErrorResponse } from "@gwop/sdk/errors";

if (err instanceof ErrorResponse && err.error.code === ErrorCode.InvoiceNotFound) {
  // ...
}
Code Status Meaning
ErrorCode.Unauthorized 401 Invalid, revoked, or missing API key
ErrorCode.Forbidden 403 Valid key but merchant account not active
ErrorCode.ValidationError 400 Request body failed validation
ErrorCode.InvoiceNotFound 404 Invoice doesn't exist or not visible to this merchant
ErrorCode.InvoiceCancelNotAllowed 400 Cannot cancel — invoice is not OPEN
ErrorCode.AuthIntentNotSettled 402 Agent hasn't paid the auth challenge yet
ErrorCode.AuthIntentNotFound 404 Auth intent doesn't exist
ErrorCode.AuthIntentExpired 409 Auth intent TTL exceeded — create a new one
ErrorCode.AuthIntentUsed 409 Auth intent already exchanged for a JWT
ErrorCode.SessionNotFound 404 Session doesn't exist
ErrorCode.IdempotencyConflict 409 Idempotency key reused with different parameters
ErrorCode.RateLimited 429 Too many requests — check Retry-After header

Configuration

const gwop = new Gwop({
  merchantApiKey: "sk_m_...",   // or set GWOP_MERCHANT_API_KEY env var
  webhookSecret: "whsec_...",   // or set GWOP_WEBHOOK_SECRET env var
  timeoutMs: 30_000,            // request timeout
  debugLogger: console,         // or set GWOP_DEBUG=true
});

The SDK retries failed requests with exponential backoff automatically. All methods return { headers, result }. See docs.gwop.io for retry configuration, response shapes, and advanced usage.

Concepts

Two invoice IDs

Every invoice has two identifiers:

Field Format Used by
id UUID (ba7bc94a-...) Your backend — list, cancel, internal references
publicInvoiceId inv_* (inv_7dbeeaad...) Payers and agents — get invoice, payment URLs

Platform fee

The backend adds a 2.5% platform fee to amountUsdc. A request for 5_000_000 ($5.00) results in an invoice total of 5_125_000 ($5.125). The fee breakdown is injected into metadata automatically.

Links


Built by the Gwop team. MIT License.

About

Node.js SDK for the Gwop API.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors