API keys: create, list and revoke X-Client-Secret
A MONA Pay API key is the client_secret string generated by POST /api/v1/client-keys/generate. The system shows the secret exactly once at creation and then stores only a hash, so it cannot be viewed again. Send the secret in the X-Client-Secret header on every POST, PUT and DELETE request (together with the Bearer token). If you lose it, create a new key and revoke the old one; an account can have many keys.
When you need a key
| Action | Needs Bearer | Needs X-Client-Secret |
|---|---|---|
| Sign up, log in | no | no |
| Reading data (GET): transactions, VAs, webhook logs | yes | no |
| Writing data (POST, PUT, DELETE): create VA, create QR, configure webhooks/Telegram, change password | yes | yes |
Clean practice: one key per integrated system (store, accounting software, bot), named after that system. If one system leaks its key, revoke that key only; the others are unaffected.
POST /api/v1/client-keys/generate
Needs Bearer.
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | no | A memorable name, default Default Key |
Response 200:
{
"success": true,
"message": "Key generated successfully",
"data": {
"id": "0190a1b2-...",
"client_id": "0190a0f0-...",
"client_secret": "mps_...secret-shown-only-once",
"name": "Online store",
"is_active": true,
"created_at": "2026-08-28T10:30:00"
}
}
Store client_secret in an environment variable (MONA_SECRET) right away. Once the screen is closed it cannot be retrieved.
cURL
curl -X POST https://api.monapay.vn/api/v1/client-keys/generate \
-H "Authorization: Bearer $MONA_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"Online store"}'
PHP
<?php
function monaPost(string $path, array $body, string $token, ?string $secret = null): array {
$headers = "Content-Type: application/json\r\nAuthorization: Bearer $token\r\n";
if ($secret) $headers .= "X-Client-Secret: $secret\r\n";
$raw = file_get_contents('https://api.monapay.vn' . $path, false, stream_context_create([
'http' => ['method' => 'POST', 'header' => $headers, 'content' => json_encode($body), 'ignore_errors' => true],
]));
return json_decode($raw, true) ?? ['success' => false, 'message' => 'Could not read response'];
}
$res = monaPost('/api/v1/client-keys/generate', ['name' => 'Online store'], getenv('MONA_TOKEN'));
$clientSecret = $res['data']['client_secret'] ?? null; // write to .env, shown only once
Node
const res = await fetch('https://api.monapay.vn/api/v1/client-keys/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.MONA_TOKEN}` },
body: JSON.stringify({ name: 'Online store' }),
});
const { success, data, message } = await res.json();
if (!success) throw new Error(message);
console.log('Write to .env:', data.client_secret); // shown only once
GET /api/v1/client-keys/list
Lists the account’s keys without secrets. Needs Bearer.
curl https://api.monapay.vn/api/v1/client-keys/list \
-H "Authorization: Bearer $MONA_TOKEN"
{
"success": true,
"message": "Keys retrieved",
"data": [
{ "id": "0190a1b2-...", "client_id": "0190a0f0-...", "name": "Online store", "is_active": true, "created_at": "2026-08-28T10:30:00" }
]
}
DELETE /api/v1/client-keys/destroy/{key_id}
Revokes a key. Needs Bearer. Requests using this key’s secret are rejected immediately after revocation.
curl -X DELETE https://api.monapay.vn/api/v1/client-keys/destroy/0190a1b2-... \
-H "Authorization: Bearer $MONA_TOKEN"
Response: {"success": true, "message": "Key destroyed", "data": null}. A key that does not exist or does not belong to the account returns 404.
Using the secret in a write request
curl -X POST https://api.monapay.vn/api/v1/client-webhooks \
-H "Authorization: Bearer $MONA_TOKEN" \
-H "X-Client-Secret: $MONA_SECRET" \
-H 'Content-Type: application/json' \
-d '{"name":"Online store","webhook_url":"https://shop.example.com/webhook/monapay","auth_type":"HMAC_SHA256","secret_key":"your-hmac-secret"}'
Note the two different secrets: client_secret (X-Client-Secret) is for you to call the MONA Pay API; secret_key in a webhook configuration is for MONA Pay to sign payloads sent to your server (see Webhook security). They should be different strings.
Enforcement status (checked 28/08/2026): the production server does not yet reject write calls that lack X-Client-Secret; the enforcing update is written and waiting to be deployed. Send the header now so nothing changes for you when it is switched on.
Common errors
| HTTP | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong Bearer token | Log in again for a new token |
| 404 | key_id does not exist or does not belong to the account |
Call /client-keys/list for the right id |
| 422 | key_id is not a valid UUID |
Copy the id exactly from the list |
Keys can also be created and revoked in the dashboard under API Keys, which includes a “copy prompt for AI agent” block to paste into Claude Code, Codex or Cursor.