QRQR Code Agency
API reference

Keys management

Create, list, rotate, and revoke API keys via the JWT-authenticated keys endpoints.

Keys management

Self-service endpoints for creating, listing, rotating, and revoking your API keys.

important: JWT auth, not key auth These endpoints require an Authorization: Bearer <jwt> header, not an X-Api-Key header. The intent: the human user (logged into the dashboard) manages their keys; their machines (using a key) cannot self-create more keys.

List keys

GET /api/v1/keys/
Authorization: Bearer <jwt>

Response: 200 OK

{
 "keys": [
 {
 "id": 7,
 "name": "production-server-1",
 "key_prefix": "smk_aBc12dEf",
 "plan": "starter",
 "is_active": true,
 "created_at": "2026-04-26T18:30:00Z",
 "last_used_at": "2026-05-08T14:14:22Z",
 "revoked_at": null
 },
 {
 "id": 4,
 "name": "old-test-key",
 "key_prefix": "smk_z9YxWvUt",
 "plan": "free",
 "is_active": false,
 "created_at": "2026-03-01T10:00:00Z",
 "last_used_at": "2026-03-15T14:20:11Z",
 "revoked_at": "2026-04-01T09:00:00Z"
 }
 ]
}

The raw secret is never returned, only the prefix.

Create a key

POST /api/v1/keys/
Authorization: Bearer <jwt>
Content-Type: application/json

{
 "name": "production-server-1",
 "plan": "starter"
}

Body

FieldTypeRequiredDescription
namestringyesHuman-readable label, max 100 chars
planenumfree (default), starter, pro, agency, enterprise

The plan must be at most the workspace's current subscription tier. You cannot create a Pro key on a Starter subscription.

Response: 201 Created

{
 "id": 7,
 "name": "production-server-1",
 "raw_key": "smk_aBc12dEf3GhI4jKlMnOpQrStUvWxYz67890",
 "key_prefix": "smk_aBc12dEf",
 "plan": "starter",
 "warning": "Store this key securely. It will NOT be shown again."
}

danger: One-time disclosure raw_key appears in the response only once. We store the SHA-256 hash and cannot recover the original. Lose it -> revoke and create a new one.

400 Bad Request

{ "name": "Required." }
{ "plan": "Plan 'pro' exceeds your subscription tier 'starter'." }

Rotate a key

POST /api/v1/keys/{id}/rotate/
Authorization: Bearer <jwt>

Issues a new raw key, invalidates the previous one immediately. The key's id, name, plan, and audit history are preserved.

Response: 200 OK

{
 "id": 7,
 "name": "production-server-1",
 "raw_key": "smk_NEW_kEyHeRe...",
 "key_prefix": "smk_NEWkEy",
 "plan": "starter",
 "rotated_at": "2026-05-08T14:00:00Z",
 "warning": "Store this key securely. It will NOT be shown again."
}

Use this for the deploy new -> verify -> deploy old key removed rotation pattern. The old hash is destroyed at rotation time, so any client still holding the old key gets 401.

Revoke a key

DELETE /api/v1/keys/{id}/
Authorization: Bearer <jwt>

Response: 204 No Content

After revocation, the key is permanently rejected with 401. The audit trail (which calls were made) is preserved for billing and forensics.

404 Not Found

The key does not exist or does not belong to you.

Examples

cURL

# Create
curl -X POST https://api.qrstudio.agency/api/v1/keys/ \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{"name":"laptop","plan":"starter"}'

# List
curl https://api.qrstudio.agency/api/v1/keys/ \
-H "Authorization: Bearer <jwt>"

# Rotate
curl -X POST https://api.qrstudio.agency/api/v1/keys/7/rotate/ \
-H "Authorization: Bearer <jwt>"

# Revoke
curl -X DELETE https://api.qrstudio.agency/api/v1/keys/3/ \
-H "Authorization: Bearer <jwt>"

JavaScript (in-dashboard)

const create = await fetch("/api/v1/keys/", {
method: "POST",
credentials: "same-origin",
headers: {
"Authorization": `Bearer ${jwt}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "laptop", plan: "starter" }),
});
const { raw_key } = await create.json();
showSensitiveOnce(raw_key);

See also

On this page