Authentication

Every request carries a bearer token. This page covers how to get one, what it can do, and how to keep it valid.

API keys

Create your account, then generate an API key from the dashboard — every request to the API carries it as a bearer token. A key is shown once, at creation, and can be scoped to specific organizations and revoked independently of every other key on the account.

Under the hood a key is exchanged for a short-lived access token the same way a password is: see Bearer tokens below.

Bearer tokens

Authenticate every request with an Authorization: Bearer header. Tokens come from POST /auth/login and expire after one hour — exchange the refresh token for a new one before then rather than logging in again on every request.

Log in

curl -X POST https://api.kovalinq.com/auth/login \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "email": "you@youragency.com",
      "password": "your-password"
    }'

Refreshing an expired token

An access token past its expires_in window is rejected with 401 AUTHENTICATION_FAILED. Exchange the refresh token for a new access token instead of logging in again — the refresh token itself is long-lived and does not rotate on every use.

Refresh

curl -X POST https://api.kovalinq.com/auth/refresh \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "refresh_token": "sp_rt_9c4e1b8a2f3d..."
    }'

Scopes

Every key carries scopes, and a request outside its scopes is refused with 403 INSUFFICIENT_SCOPE. Create narrowly-scoped keys for narrowly-scoped integrations — a key that only ever sends messages should not also be able to register webhooks.

ScopeGrants
organizations:readList and read organizations
organizations:writeCreate and update organizations
messages:writeSend messages and upload media
templates:writeCreate and submit templates
flows:writeCreate and publish flows
webhooks:writeRegister and delete webhook endpoints

Webhook secrets

Every webhook endpoint gets its own signing_secret when you register it. Each delivery carries an HMAC-SHA256 signature of the raw request body in the X-SalePilot-Signature header — compute the same HMAC over the body you received and compare it before trusting the payload.

In code

Node.js

const response = await fetch("https://api.kovalinq.com/auth/login", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "email": "you@youragency.com",
    "password": "your-password"
  }),
});

const data = await response.json();

Python

import json
import requests

body = json.loads('{"email":"you@youragency.com","password":"your-password"}')

response = requests.post(
    "https://api.kovalinq.com/auth/login",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json=body,
)
data = response.json()

Keeping keys secure

  • Never ship a live token in client-side JavaScript, a mobile app binary, or a public repository.
  • Store tokens in your server’s secret manager, not in application code or version control.
  • Rotate a key immediately if it may have leaked — an old key can be revoked without affecting others.
  • Restrict a key to specific organizations when it only needs to act on a subset of your clients — see Organizations.