Geldstuck API keys come in publishable/secret pairs, one per tenant. Both halves are required on every request - the pk_ identifies the tenant, and the sk_ authenticates it.
Anatomy of a key
sk_live_51HxR9z7kZ4M3yB2wQpT8vNjKc6Y7xW9aP5D
│ │ │
│ │ └── Random 32-char body
│ └────── Environment: live or test
└───────── Key type: pk (publishable) or sk (secret)
Creating keys
Every tenant starts with one key pair. Create additional keys from the dashboard or via API - one per service, one per environment is a good default.
curl https://api.geldstuck.com/v1/tenants/$TENANT_ID/api-keys \
-H "x-api-key: $GELDSTUCK_PUBLIC_KEY" \
-H "x-api-secret: $GELDSTUCK_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"label": "billing-worker"}'
Response:
{
"id": "key_01HX3ZDE...",
"label": "billing-worker",
"publicKey": "pk_live_51H...",
"secretKey": "sk_live_51H...",
"createdAt": "2026-04-22T10:00:00.000Z",
"status": "active"
}
secretKey is returned only on creation. We store a hash, not the key itself - so if you lose it, you’ll need to regenerate.
Scoping keys
Label your keys by service so you can revoke with surgical precision:
| Label | Example use |
|---|
web-api | Your Node.js API calling Geldstuck |
kyc-worker | Background worker handling KYC webhooks |
admin-scripts | Operator scripts run from a bastion |
ci | End-to-end tests in CI |
Rotating & revoking
Regenerate
POST /tenants/:tenantId/api-keys/:keyId/regenerate returns a new pk_/sk_ pair. The old pair stays live for 10 minutes to let in-flight deploys complete.
Revoke
POST /tenants/:tenantId/api-keys/:keyId/revoke invalidates the key pair immediately. Use this when a key is leaked.
Both operations emit webhook events (api_key.regenerated, api_key.revoked) so your ops tooling can track key lifecycles.
Rate limits
Rate limits apply per key, not per tenant. Creating separate keys for high-throughput services means they won’t compete for quota. See rate limits.