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://lora-api.agiletelecom.com/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"
}

:::info 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://lora-api.agiletelecom.com/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"
}

:::warning 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. :::

:::note 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://lora-api.agiletelecom.com/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://lora-api.agiletelecom.com/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://lora-api.agiletelecom.com/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://lora-api.agiletelecom.com/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://lora-api.agiletelecom.com/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​

{
"status": "fail",
"data": {
"authentication": "Invalid or missing 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​