API Docs

Authentication, in two flavors

API keys for server-to-server. Bearer tokens for short-lived integrations. Same endpoints, same response shapes.

Overview

Two schemes, one API

Inksong accepts two authentication schemes against the same set of endpoints. Long-lived API keys are the recommended path for server-to-server integrations — back-end workers, cron jobs, internal tools, anything running outside a browser. Short-lived Bearer access tokens are what the web app uses, and they’re a reasonable fit for OAuth-style flows when those ship.

Pick API keys for back-end integrations. Pick Bearer tokens when you have a real user session in front of a browser. The endpoints don’t care which header you send — they only care that it’s valid.

API keys

Long-lived, server-side

Every Inksong account is provisioned with one API key. View or rotate it from your dashboard. Keys are prefixed ink_followed by 48 hexadecimal characters — 52 characters total, generated from a cryptographically strong source.

Authenticate by sending the key in the X-API-Key header. API keys do not expire on a schedule. If a key is compromised, regenerate it yourself from the dashboard — the old key stops working immediately and a new one is shown to you once for copying.

curl

curl https://api.inksong.app/api/v1/documents/ \
  -H "X-API-Key: ink_YOUR_KEY_HERE"

Python

import os
import requests

response = requests.get(
    "https://api.inksong.app/api/v1/documents/",
    headers={"X-API-Key": os.environ["INKSONG_API_KEY"]},
)
response.raise_for_status()
print(response.json())

JavaScript

const response = await fetch("https://api.inksong.app/api/v1/documents/", {
  headers: { "X-API-Key": process.env.INKSONG_API_KEY },
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
console.log(data);

Bearer tokens

Short-lived, browser-context

Bearer access tokens are issued by POST /api/v1/auth/login in exchange for an email + password, and refreshed via POST /api/v1/auth/refresh. Access tokens expire after 15 minutes. Refresh tokens are issued as httpOnly cookies with the Secure and SameSite=Strictflags — they’re not exposed to JavaScript on purpose.

Authenticate by sending the access token in the Authorization: Bearer <token>header. This is what the web app uses, and it’s the reasonable choice for any integration that already has a user signed in via the dashboard.

curl

# 1. Log in
curl -X POST https://api.inksong.app/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"..."}'
# → {"access_token":"eyJ...","token_type":"bearer"}

# 2. Authed request
curl https://api.inksong.app/api/v1/documents/ \
  -H "Authorization: Bearer eyJ..."

Python

import requests

session = requests.Session()
login = session.post(
    "https://api.inksong.app/api/v1/auth/login",
    json={"email": "you@example.com", "password": "..."},
)
login.raise_for_status()
token = login.json()["access_token"]

response = session.get(
    "https://api.inksong.app/api/v1/documents/",
    headers={"Authorization": f"Bearer {token}"},
)
print(response.json())

JavaScript

const login = await fetch("https://api.inksong.app/api/v1/auth/login", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ email: "you@example.com", password: "..." }),
  credentials: "include",
});
const { access_token } = await login.json();

const response = await fetch("https://api.inksong.app/api/v1/documents/", {
  headers: { Authorization: `Bearer ${access_token}` },
});
console.log(await response.json());

Choosing

Which scheme to pick

If your code runs on a server you control — a Lambda, a Rails worker, a Django management command, a bash script in a CI pipeline — use an API key. It’s simpler, doesn’t expire, and doesn’t require a login round-trip.

If your code runs in a browser tab, use a Bearer token. The token is short-lived, the refresh token is httpOnly, and the user is in front of the browser to recover from any edge case.

Most external integrations want API keys. Reach for Bearer tokens only when you specifically need a user session.

Security

Keep keys out of client code

Never embed an API key in client-side JavaScript, mobile binaries, or any artifact shipped to a user’s machine. Never commit a key to git. Keys belong in environment variables or a secrets manager (1Password, AWS Secrets Manager, HashiCorp Vault, Doppler — whatever your stack uses).

If a key leaks — pushed to a public repo, pasted into a Slack channel, captured in a screenshot — rotate it immediately from the API keys page. Rotation is instant: the old key starts returning 401 Unauthorized on the next request, and the new key is shown to you once for copying.

On the roadmap

IP allowlisting on a per-key basis is something we want to add — you’ll be able to scope a key to a CIDR range and reject everything else at the edge. It’s not shipped yet.