Skip to main content

UC-014 — API Key Management

FieldValue
IDUC-014
GoalCreate, inspect and revoke API Keys for your account
ChannelAll
ComplexityBasic
Estimated time5 minutes
APIs involvedGET /api/partner-gateway/v1/authentication, GET /api/partner-gateway/v1/authentication/me, POST /api/partner-gateway/v1/authentication, DELETE /api/partner-gateway/v1/authentication/{id}

Real-world scenarios​

  • Developer onboarding: The team lead creates a dedicated API Key for a new integration, scoped to only the operations that integration needs.
  • Periodic key rotation: FinSecure rotates its API Keys every 90 days as required by internal security policy.
  • Revoke a compromised key: A developer accidentally committed an API Key to a public repository and needs to revoke it immediately.

Management flow​

The diagram shows the complete API Key lifecycle: scope check, creation, listing and revocation.

Prerequisites​

  • Active account on the Qlara platform
  • At least one existing API Key to authenticate management calls
  • The calling key must be authorized for the AUTHENTICATION operation

Step 1 — Check what your current key can do​

Before creating a key, inspect the one you are calling with. A new key can only be granted operations the calling key already holds, so this tells you the maximum scope available to you.

curl -X GET https://api.qlara.ai/api/partner-gateway/v1/authentication/me \
-H "X-Api-Key: YOUR_API_KEY"

Response — Current key​

{
"id": 1,
"companyId": 100,
"userId": 42,
"username": "api-user",
"trafficType": 0,
"operations": ["SMS", "RCS", "WHATSAPP"],
"creationDate": "2025-03-10 14:30:00.000+0100",
"lastUpdateDate": "2025-03-10 14:30:00.000+0100"
}
The key is never sent in the URL

The key is read from the X-Api-Key header, so this endpoint takes no parameters and never echoes the key value back. It replaces the removed GET /authentication/{value}, which took a raw key in the URL.

Step 2 — Create a new API Key​

Call the creation endpoint to generate a new key.

curl -X POST https://api.qlara.ai/api/partner-gateway/v1/authentication \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"userId": 4618,
"trafficType": 0,
"operations": ["SMS", "RCS", "WHATSAPP"]
}'
FieldRequiredDescription
userIdYesID of the user the key belongs to. Keys without a userId are orphan and cannot be deleted through this API.
operationsYesOperations the key may perform. Must be a non-empty subset of the operations held by the calling key.
trafficTypeNoTraffic type identifier (0 = standard). Leave at 0 unless instructed otherwise.

Allowed values for operations: CONTACTS, SOCIALS, INBOX, AUTOMATION, MEDIA, MESSAGES, EMAIL, REPORT, CALENDAR, SUBSCRIPTION, AUTHENTICATION, RCS, WHATSAPP, SMS, CAMPAIGNS, EXPORTS, WEBHOOKS.

Response — Key created​

{
"id": 1,
"companyId": 100,
"apiKey": "ak_live_abc123def456",
"username": null,
"trafficType": 0,
"operations": ["SMS", "RCS", "WHATSAPP"],
"creationDate": "2025-03-10 14:30:00.000+0100"
}
Save the key immediately

The apiKey field is shown only at creation time. Copy it and store it in a secret manager (e.g. Vault, AWS Secrets Manager). It cannot be retrieved again through any endpoint.

No username on creation

Keys created here are identified by their id and userId and carry no username, so username in the response is always null. A username sent in the request body is ignored.

Behind the scenes — Key generation and scoping
  1. Authorization: The gateway resolves the calling key and checks it is authorized for the AUTHENTICATION operation.
  2. Scope check: The requested operations are compared against the calling key's own operations. Anything beyond them is refused with 403 — a key cannot grant privileges it does not have.
  3. Generation: A key string is generated and returned once, in this response only.
  4. Association: The key is linked to the company of the calling key and to the userId you supplied.

Step 3 — List existing keys​

Retrieve the API Keys associated with your account. Add the optional userId query parameter to filter by owner.

curl -X GET https://api.qlara.ai/api/partner-gateway/v1/authentication \
-H "X-Api-Key: YOUR_API_KEY"

Response — Key list​

[
{
"id": 1,
"companyId": 100,
"userId": 4618,
"username": "api-user",
"trafficType": 0,
"operations": ["SMS", "RCS", "WHATSAPP"],
"creationDate": "2025-03-10 14:30:00.000+0100",
"lastUpdateDate": "2025-03-10 14:30:00.000+0100"
},
{
"id": 2,
"companyId": 100,
"userId": 5172,
"username": null,
"trafficType": 0,
"operations": ["CONTACTS", "CAMPAIGNS", "MESSAGES"],
"creationDate": "2026-01-15 09:00:00.000+0100",
"lastUpdateDate": "2026-01-15 09:00:00.000+0100"
}
]

The full key value is never returned by this endpoint — only its metadata.

Step 4 — Revoke a compromised key​

Delete a key that should no longer be used. The key id goes in the path.

curl -X DELETE https://api.qlara.ai/api/partner-gateway/v1/authentication/1 \
-H "X-Api-Key: YOUR_API_KEY"

Response — Key revoked​

204 No Content — the response has no body. A key that does not exist returns 404.

Behind the scenes — What happens after revocation
  1. Immediate invalidation: The key is removed from the Key Store. Subsequent requests using it receive 401 Unauthorized.
  2. No rollback: Deletion is irreversible. To restore access, create a new key.
  3. Orphan keys: A key stored without a userId cannot be deleted through this API — this is why userId is required at creation.

Expected result​

StepActionResult
1GET /authentication/me200 OK with the calling key's operations
2POST /authentication201 Created, full apiKey returned once
3GET /authentication200 OK with the array of keys (no key values)
4DELETE /authentication/{id}204 No Content

Complete end-to-end example​

Scenario FinSecure: quarterly key rotation.

BASE=https://api.qlara.ai/api/partner-gateway/v1

# 1. Check the scope available to the calling key
curl -s -X GET "$BASE/authentication/me" \
-H "X-Api-Key: $CURRENT_KEY" | jq '.operations'

# 2. Create the new key, scoped to the same operations
NEW_KEY=$(curl -s -X POST "$BASE/authentication" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $CURRENT_KEY" \
-d '{
"userId": 4618,
"trafficType": 0,
"operations": ["SMS", "RCS", "WHATSAPP"]
}' | jq -r '.apiKey')

echo "New key: $NEW_KEY"

# 3. Verify the new key works and inspect its scope
curl -s -X GET "$BASE/authentication/me" \
-H "X-Api-Key: $NEW_KEY" | jq '{id, operations}'

# 4. Revoke the old key by its id
curl -s -o /dev/null -w "%{http_code}\n" \
-X DELETE "$BASE/authentication/$OLD_KEY_ID" \
-H "X-Api-Key: $NEW_KEY"

Variants​

Create a least-privilege key per integration​

Scope each key to the smallest set of operations that covers its job:

# Sender-only key
curl -X POST https://api.qlara.ai/api/partner-gateway/v1/authentication \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"userId": 4618, "operations": ["SMS"]}'

# Marketing automation key
curl -X POST https://api.qlara.ai/api/partner-gateway/v1/authentication \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"userId": 4618, "operations": ["CONTACTS", "CAMPAIGNS", "MESSAGES"]}'

Common errors​

400 Bad Request — Missing or empty operations​

{
"status": "fail",
"data": {
"operations": "operations is required: a key with no operations cannot authenticate any request"
}
}

Solution: Send a non-empty operations array. A key with no operations cannot authenticate anything.

401 Unauthorized — Invalid key​

{
"error": "Invalid API Key"
}

Solution: Verify that the X-Api-Key header is present and that the key used to manage other keys is still active and authorized for the AUTHENTICATION operation.

403 Forbidden — Requested operations exceed your scope​

{
"status": "fail",
"data": {
"operations": "Requested operations exceed the caller's own scope"
}
}

Solution: A key cannot grant privileges it does not have. Call GET /authentication/me to see what the calling key holds, then request a subset of those operations.

404 Not Found — Key does not exist​

Solution: Check the id in the path against the list returned by GET /authentication. Keys stored without a userId cannot be deleted through this API.

Next steps​

References​