Documentation

Issuing licenses

POST /v1/issue mints a fresh license key and creates or dedupes a customer record. It's the right endpoint to call from a webhook handler (Stripe, Paddle, your own checkout) the moment a payment succeeds.

The API key used to call this endpoint must have FULL or ISSUE_ONLY scope.

Request

POST /v1/issue
Host: api.keystack.dev
Authorization: Bearer ak_live_4f2a...
X-KeyStack-Timestamp: 1731600000
X-KeyStack-Signature: 7b3d8a1f...
Content-Type: application/json
 
{
  "planCode": "lifetime-pro",
  "customer": {
    "email": "alice@example.com",
    "name": "Alice Smith",
    "externalId": "stripe_cust_abc"
  },
  "duration": "lifetime",
  "maxActivations": 3,
  "notes": "Black Friday order",
  "metadata": {
    "stripeCheckoutId": "cs_test_abc",
    "campaign": "launch-2026"
  }
}

Fields

FieldTypeRequiredNotes
planCodestringyesMust match an existing plan in the application.
customer.emailstringyesUsed for dedup + delivery email. Lowercased before lookup.
customer.namestringnoCosmetic.
customer.externalIdstringnoYour internal customer ID (e.g. Stripe customer). Merged non-destructively on re-issue.
customer.metadataobjectnoFree-form JSON merged into the customer row.
duration"lifetime" | "<n>d" | ISO 8601 datetimenoOverrides the plan's default duration. Use "30d" / "365d" / "2027-01-01T00:00:00Z".
maxActivationsnumbernoOverrides the plan's default activation limit.
notesstringnoInternal-only note shown in the dashboard.
metadataobjectnoFree-form JSON, queryable in the dashboard.

Response

{
  "id": "ckxz1ab2c0000abcd",
  "key": "KS-7F3K9-9XYLM-4N2A1-Q7C9F-WT2K",
  "status": "ACTIVE",
  "expiresAt": null,
  "customer": {
    "id": "cus_8a3f...",
    "email": "alice@example.com"
  }
}

Error codes

HTTPcodeMeaning
400validation/invalid-inputOne or more fields failed validation (check details).
401api/key-invalidAPI key was not recognised.
401api/signature-invalidHMAC signature did not match.
401api/timestamp-replayThis signature was already used or X-KeyStack-Timestamp is too old.
403authz/role-insufficientAPI key scope can't issue licenses.
403quota/licenses-exceededYour platform plan has hit its active-license cap.
404common/not-foundplanCode doesn't exist in this application.
429common/rate-limitedSlow down — back off and retry.