Documentation menu

Authentication

The MASK API uses API keys to authenticate requests. Each key is scoped to a workspace and can be restricted to specific permissions. All requests must include a valid API key in the Authorization header.

API Keys

API keys are created from your workspace settings. Navigate to Settings → API Keys → Create Key. Every key belongs to a single workspace and can only access resources within that workspace.

A key is the prefix mk_ followed by 64 hexadecimal characters. There is one environment: every key creates real links and records real analytics, so anything you send while developing lands in the workspace the key belongs to. There is no test mode and no sandbox key.

To keep development away from production data, create a separate workspace for it and mint a key there.

The API is a paid feature. A key only works while its workspace is on Growth or above; if the workspace drops to Hobby, existing keys stop authenticating and return 401 Unauthorized.

Creating API Keys

To create an API key:

  1. Go to your workspace Settings → API Keys.
  2. Click Create Key.
  3. Enter a descriptive name (e.g. "Production Backend" or "CI Pipeline").
  4. Select the scopes your integration requires.
  5. Click Create. The full key is displayed once — copy it immediately.

The key value is only shown at creation time. If you lose it, you must create a new key.

Scopes

Each API key is assigned one or more scopes that control what operations it can perform. Follow the principle of least privilege: only grant the scopes your integration needs.

Fourteen scopes exist, and this is all of them. Several are reserved: they can be granted to a key, but no endpoint on /api/v1 requires them today, so granting one adds nothing. The Reserved column says which.

ScopeReservedDescription
links:readNoList and retrieve links
links:writeNoCreate, update, and delete links. Also required by POST /api/v1/qr, because each QR code also creates a short link.
conversions:writeNoRecord conversions against your links, QR codes and campaigns
conversions:readNoRead conversion totals, rates and values, and read goals and goal reports
pages:readYesBio pages are managed in the dashboard; no endpoint reads them
pages:writeYesBio pages are managed in the dashboard; no endpoint writes them
analytics:readYesClick and view analytics are read in the dashboard and the CSV export, not over the API
qr:readYesNo endpoint requires it
qr:writeYesNo endpoint requires it. Use links:write for POST /api/v1/qr.
team:readYesTeam members are managed in the dashboard
team:writeYesTeam members are managed in the dashboard
webhooks:readYesWebhook subscriptions are managed in the dashboard
webhooks:writeYesWebhook subscriptions are managed in the dashboard
domains:readYesCustom domains are managed in the dashboard

If a request requires a scope that the API key does not have, the API returns 403 Forbidden. The body is a single error string. The links and QR endpoints return it unadorned; the conversions and goals endpoints name the scope they wanted.

Insufficient scope response
{
  "error": "Insufficient scope"
}
Insufficient scope, conversions and goals
{
  "error": "Insufficient scope. This endpoint requires \"conversions:read\"."
}

Bearer Token Usage

Include your API key in the Authorization header using the Bearer scheme. The key must be sent with every request.

cURL
curl https://mask.pk/api/v1/links \
  -H "Authorization: Bearer mk_3f9a1c7e5b208d46a1f0c93e78b25d4610fa8c3729de154bb06e7a9f2c8d3510"
Node.js (fetch)
const response = await fetch("https://mask.pk/api/v1/links", {
  headers: {
    Authorization: `Bearer ${process.env.MASK_API_KEY}`,
    "Content-Type": "application/json",
  },
});

const { data } = await response.json();
Python (requests)
import os
import requests

response = requests.get(
    "https://mask.pk/api/v1/links",
    headers={"Authorization": f"Bearer {os.environ['MASK_API_KEY']}"},
)

data = response.json()["data"]

Requests without a valid API key receive a 401 Unauthorized response. The same body is returned whether the key is missing, malformed, revoked, expired, or belongs to a workspace that is no longer on a paid plan.

401 response
{
  "error": "Unauthorized"
}

Key Rotation

To rotate a key without downtime, follow this procedure:

  1. Create a new API key with the same scopes as the existing key.
  2. Update your application to use the new key.
  3. Deploy the change and verify requests succeed with the new key.
  4. Revoke the old key from Settings → API Keys.

Once a key is revoked, all requests using that key immediately return 401 Unauthorized. Revocation is irreversible.

Nothing expires or disables a key on its own. Each key shows when it was last used, so you can spot keys nothing is calling any more, but reviewing and revoking them is a manual step.

Security Best Practices

Use environment variables. Never hardcode API keys in source code. Store them in environment variables or a secrets manager like AWS Secrets Manager, HashiCorp Vault, or Vercel Environment Variables.

Scope narrowly. Grant only the permissions your integration requires. A conversion reporting job only needs conversions:read. A checkout integration that records outcomes only needs conversions:write. A link creation service only needs links:write.

Rotate regularly. Rotate production keys every 90 days. Create the new key before revoking the old one to avoid downtime.

Separate environments. There is no test key, so use a separate workspace for development and staging and give it its own key. A key can only reach the workspace it was created in, which is what keeps the two apart.

Never expose client-side. API keys must only be used in server-side code. Never include them in frontend JavaScript, mobile app bundles, or public repositories.

Monitor usage. Check the API Keys page in your workspace settings to see when each key was last used, and the usage panel beside it for request counts, errors and rate-limit hits per key over the last 7, 30 or 90 days. Calling IP addresses are not recorded, so usage cannot tell you where a request came from.

Storing keys in environment variables
# .env (add to .gitignore)
MASK_API_KEY=mk_3f9a1c7e5b208d46a1f0c93e78b25d4610fa8c3729de154bb06e7a9f2c8d3510

# Usage in Node.js
const apiKey = process.env.MASK_API_KEY;