Introduction

Authentication

Bearer keys, scopes, expiry and what to do the moment a secret leaks.

Bearer tokens

The API authenticates with a single API key sent as a bearer token. There is no OAuth dance, no session, no refresh token. Just one header on every request.

  • Keys are prefixed la_ followed by 32 characters.
  • Use HTTPS. The API is only served over TLS.
  • A key identifies a publisher account, not a person. The same key can reach every channel that account owns, subject to its scopes.

When it goes wrong

A missing, malformed, paused, expired or revoked key returns 401, and so does a key whose publisher account is not active. A valid key that lacks the scope for the endpoint returns 403, and tells you exactly which scope was missing.

GET /v1/programs?size=10 HTTP/1.1
Host: api.linkapprove.com
Authorization: Bearer la_9f2c4a71e8b350d6c1f4a7e2b8d90c53
Content-Type: application/json

Verify a key

GEThttps://api.linkapprove.com/v1/meany

Returns the key making the request: its name, last four characters, scopes, IP allowlist and expiry, plus callerIp, the address we saw the request come from. It needs no particular scope, so it is the quickest way to confirm a new or rolled key works, and to find out exactly what to put in the allowlist.

Like every authenticated call it counts towards your rate limit and quota and returns the X-RateLimit-* and X-Quota-* headers. It keeps working after the monthly quota runs out, so you can always check where you stand.

curl "https://api.linkapprove.com/v1/me" \
  -H "Authorization: Bearer $LINKAPPROVE_API_KEY"
Try it
GEThttps://api.linkapprove.com/v1/me

Held in this tab's session storage only, and reused by every runner on the site.

This panel replays the documented sample response and does not call the API. Copy a code sample to send a real request with your key.

Scopes

Scopes are granted per key and follow a resource:access shape. Write implies read, so granting links:write also grants links:read. Two resources are read-only by nature and have no write scope at all.

Available scopes
programs:readread

Browse the catalogue and see your own applications.

programs:writewrite

Apply to programs on behalf of a channel.

links:readread

List the tracking links already issued to you.

links:writewrite

Mint new tracking links against approved programs.

channels:readread

List your channels and their verification state.

channels:writewrite

Submit new channels and run the verification check.

reports:readread

Clicks, conversions and commission. Read-only.

payments:readread

Your balance, credited commissions and withdrawal history. Read-only.

webhooks:readread

List registered endpoints and their delivery history.

webhooks:writewrite

Register, change and delete endpoints, rotate their secrets, send test events and replay deliveries.

account:readread

Your profile and payment methods, with payout destinations masked. Read-only.

Grant the minimum

A reporting script that only pulls numbers should hold reports:read and nothing else. If that key leaks, the worst case is someone reads your revenue, not that they redirect your payout.
{
  "success": false,
  "message": "Invalid or revoked API key",
  "error": { "code": "invalid_api_key" }
}

Key lifecycle

Everything below is managed from Account → API Keys in the publisher dashboard. Keys cannot create other keys, and an account can hold at most 10 active keys. Revoked and expired ones do not count.

Created

The secret is displayed once, then only its last four characters are ever shown again. We store a hash.

Paused

Requests return 401 while the key is paused. Scopes, name and history are preserved. Resume it and it works again.

Rolled

A new secret is issued and the old one dies immediately. Permissions, name and reporting carry over.

Revoked

Permanent. The key stops working and cannot be brought back, so you create a new one instead.

Optional hardening

  • Expiry: 30, 90 or 365 days. The key returns 401 the moment it lapses, so treat expiry as a deadline, not a warning.
  • IP allowlist: accept a comma-separated list of up to 20 IPv4 or IPv6 addresses and CIDR ranges. Anything else gets 403 regardless of scope, and the error body names the address we saw. A host with IPv6 usually calls us over IPv6, so list its IPv6 range (for example 2001:db8:85a3:4d2::/64) as well as its IPv4 address, or force IPv4 in your client. GET /me returns the exact address to add.
# 1. roll the secret in the dashboard, then
export LINKAPPROVE_API_KEY="la_the_new_secret"

# 2. confirm the new one works
curl -s -o /dev/null -w "%{http_code}\n" \
  "https://api.linkapprove.com/v1/programs?size=1" \
  -H "Authorization: Bearer $LINKAPPROVE_API_KEY"

# 200

Handling a leak

If a secret ends up somewhere it should not be, like a public repo, a screenshot or a shared log, roll it. Rolling is faster than revoking and re-integrating, because the key keeps its identity.

  • Roll the secret from the dashboard. The old one dies instantly.
  • Update the environment variable wherever the key runs, then redeploy.
  • Check the key's last-used time and usage in the dashboard for activity you did not expect.
  • If anything looks wrong, revoke instead of rolling and open a ticket.

Never put a key in a browser

There is no public or publishable key. Anything shipped to a client is readable by anyone who opens devtools. Proxy through your own backend instead.
{
  "success": false,
  "message": "This secret was rotated on 2026-08-18",
  "error": {
    "code": "invalid_api_key",
    "rotatedAt": "2026-08-18T09:12:44Z"
  }
}