Plumaas Docs

The Plumaas API uses simple REST conventions: JSON bodies, predictable URLs, and HTTP status codes. The full OpenAPI spec is at /api/openapi.json and a Swagger UI lives at /api/docs.

Quickstart

After signing up, you get an API key. Use it as a bearer token.

cURL

# Create a payment link
curl https://plumaas.com/api/v1/charges \
  -H "Authorization: Bearer $PLUMAAS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "org_id": "ORG_UUID",
    "amount_cents": 12900,
    "currency": "USD",
    "description": "Subscription — December"
  }'

Python

pip install plumaas

from plumaas import Plumaas
p = Plumaas(api_key="psk_live_...")
charge = p.charges.create(org_id="ORG_UUID", amount_cents=12900,
                          description="Subscription — December")
print(charge["pay_url"])

Node.js

const res = await fetch("https://plumaas.com/api/v1/charges", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.PLUMAAS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    org_id: "ORG_UUID",
    amount_cents: 12900,
    currency: "USD",
    description: "Subscription — December",
  }),
});
const charge = await res.json();
console.log(charge.pay_url);

Authentication

Plumaas uses bearer tokens. Pass Authorization: Bearer YOUR_API_KEY on every request. Keys are shown once at creation — store them in your secret manager.

  • Live keys start with psk_live_
  • Test keys start with psk_test_
  • Rotate via dashboard → API keys → Generate. Old key keeps working for 24h after rotation.

Webhooks

Plumaas signs every outbound webhook with HMAC-SHA256. Verify before trusting the payload.

Signature format

X-Plumaas-Event: charge.succeeded
X-Plumaas-Timestamp: 1758432000
X-Plumaas-Signature: t=1758432000,v1=<hex_hmac_sha256>

# signed payload = f"{timestamp}.{body}"

Verifying — Python

from plumaas import verify_webhook

@app.post("/plumaas/webhook")
async def hook(req):
    body = await req.body()
    if not verify_webhook(SECRET, body, dict(req.headers)):
        raise HTTPException(401)
    event = await req.json()
    # event["event"] is one of charge.succeeded | charge.failed | ...

Verifying — Node.js

import crypto from "node:crypto";

function verify(secret, body, headers, tolerance = 300) {
  const sigHeader = headers["x-plumaas-signature"];
  if (!sigHeader) return false;
  const parts = Object.fromEntries(sigHeader.split(",").map(p => p.split("=")));
  const ts = Number(parts.t);
  if (!ts || Math.abs(Date.now()/1000 - ts) > tolerance) return false;
  const expected = crypto.createHmac("sha256", secret)
                         .update(`${ts}.`).update(body).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

Events available

  • charge.succeeded — payment captured
  • charge.failed — gateway declined; goes into the dunning queue
  • charge.refunded — full or partial refund issued
  • subscription.created — new recurring schedule
  • subscription.canceled — schedule canceled

Test before going live

Send a sample event to your endpoint from the dashboard, or via API:

curl -X POST https://plumaas.com/api/v1/webhook-endpoints/$EP_ID/test \
  -H "Authorization: Bearer $PLUMAAS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event": "charge.succeeded"}'

Ready to start?

Create an account in under a minute — KYB review happens in the background.

Get started