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/jsonVerify a key
https://api.linkapprove.com/v1/meanyReturns 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"https://api.linkapprove.com/v1/meHeld 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.
programs:readreadBrowse the catalogue and see your own applications.
programs:writewriteApply to programs on behalf of a channel.
links:readreadList the tracking links already issued to you.
links:writewriteMint new tracking links against approved programs.
channels:readreadList your channels and their verification state.
channels:writewriteSubmit new channels and run the verification check.
reports:readreadClicks, conversions and commission. Read-only.
payments:readreadYour balance, credited commissions and withdrawal history. Read-only.
webhooks:readreadList registered endpoints and their delivery history.
webhooks:writewriteRegister, change and delete endpoints, rotate their secrets, send test events and replay deliveries.
account:readreadYour profile and payment methods, with payout destinations masked. Read-only.
Grant the minimum
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
401the 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
403regardless 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 example2001:db8:85a3:4d2::/64) as well as its IPv4 address, or force IPv4 in your client.GET /mereturns 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"
# 200Handling 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
{
"success": false,
"message": "This secret was rotated on 2026-08-18",
"error": {
"code": "invalid_api_key",
"rotatedAt": "2026-08-18T09:12:44Z"
}
}