Kortilink API
REST API + Webhooks para integrar a Kortilink na tua stack. Cria links, gere bio pages, recebe eventos em tempo real, e construa o que quiseres em cima.
Base URL: https://kortilink.com
Versão: v1 (estável)
fetch, axios, requests, ou qualquer cliente HTTP. Os exemplos abaixo usam cURL e Node.js.Autenticação
Todas as chamadas a /api/v1/* exigem um header Authorization com a tua API key:
Authorization: Bearer kl_a1b2c3d4e5f6...
Cria a tua key em /dashboard → cartão "🔌 API & Webhooks". A key começa por kl_ e só é mostrada uma vez. Guarda em local seguro (gestor de passwords, env var).
👤 Obter perfil — GET /v1/me
/api/v1/meDevolve dados do utilizador associado à API key.
Exemplo
curl https://kortilink.com/api/v1/me \
-H "Authorization: Bearer kl_..."
Resposta 200
{
"id": 42,
"email": "creator@exemplo.com",
"name": "Maria",
"plan": "pro",
"isPro": true,
"createdAt": "2026-01-15T10:30:00Z",
"referralCode": "MARIA8X2"
}
📋 Listar links — GET /v1/links
/api/v1/links?limit=50&offset=0Lista links ativos do utilizador (paginado).
Query params
| Param | Tipo | Default | Max |
|---|---|---|---|
limit | int | 50 | 200 |
offset | int | 0 | — |
Resposta 200
{
"links": [
{
"short_code": "abc123",
"short_url": "https://kortilink.com/abc123",
"original_url": "https://exemplo.com/produto-muito-longo",
"title": "O meu produto",
"click_count": 42,
"createdAt": "2026-04-30T14:22:00Z"
}
],
"total": 18,
"limit": 50,
"offset": 0
}
✏️ Criar link — POST /v1/links
/api/v1/linksEncurta um URL.
Body (JSON)
| Campo | Tipo | Obrig. | Notas |
|---|---|---|---|
url | string | ✓ | Max 2048 chars |
custom_code | string | — | 2-50 chars, [a-z0-9-_] |
title | string | — | Max 200 chars |
Exemplo (Node.js)
const r = await fetch('https://kortilink.com/api/v1/links', {
method: 'POST',
headers: {
'Authorization': 'Bearer kl_...',
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: 'https://exemplo.com/black-friday',
title: 'BF 2026',
custom_code: 'bf26'
})
});
const data = await r.json();
console.log(data.short_url); // https://kortilink.com/bf26
Resposta 201
{
"short_code": "bf26",
"short_url": "https://kortilink.com/bf26",
"original_url": "https://exemplo.com/black-friday",
"title": "BF 2026",
"createdAt": "2026-04-30T15:00:00Z"
}
🔍 Obter link — GET /v1/links/:code
/api/v1/links/:codeDetalhe + estatísticas (click_count, top countries, etc).
🗑️ Apagar link — DELETE /v1/links/:code
/api/v1/links/:code{ "success": true }
🌐 Obter Bio Page — GET /v1/bio
/api/v1/bioDevolve metadados da bio page do utilizador.
{
"bio": {
"slug": "maria",
"title": "Maria · Designer",
"description": "UX em Lisboa 🇵🇹",
"avatar": "https://...",
"url": "https://kortilink.com/b/maria",
"blocks_count": 8,
"updatedAt": "2026-04-30T12:00:00Z"
}
}
📊 Analytics da Bio — GET /v1/analytics/bio
/api/v1/analytics/bio?days=30Retorna views, clicks, CTR, e breakdowns por país/device/bloco.
{
"period_days": 30,
"views": 1284,
"clicks": 387,
"ctr": 30.1,
"by_country": { "PT": 720, "BR": 320, "ES": 180, "FR": 64 },
"by_device": { "mobile": 980, "desktop": 280, "tablet": 24 },
"by_block": { "abc12def": 145, "xyz78ghi": 98 }
}
🪝 Webhooks
Recebe eventos em tempo real. Cria um webhook em /dashboard com:
- URL HTTPS que aceita POST
- Lista de eventos a receber (ou
*para todos)
Recebes um secret único por webhook. Cada delivery vem com X-Kortilink-Signature: sha256=<hmac> que assinas o body com esse secret.
Estrutura do payload
{
"event": "link.created",
"timestamp": "2026-04-30T15:00:00Z",
"data": {
"short_code": "bf26",
"short_url": "https://kortilink.com/bf26",
"original_url": "https://...",
"title": "BF 2026"
}
}
Headers
POST /your/webhook/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Kortilink-Webhooks/1.0
X-Kortilink-Event: link.created
X-Kortilink-Signature: sha256=a7b8c9...
X-Kortilink-Webhook-Id: 7
Eventos disponíveis
| Evento | Quando dispara |
|---|---|
link.created | Novo link encurtado (UI ou API) |
link.deleted | Link apagado |
link.clicked | Cada clique no link (high-volume) |
bio.published | Bio guardada/publicada |
bio.viewed | Visita à bio page |
bio.click | Click num bloco da bio |
bio.email_capture | Inscrição via bloco Email |
* | Todos os eventos (catch-all) |
🔐 Verificar assinatura
Recomendado em produção. Compara o X-Kortilink-Signature com um HMAC-SHA256 do body usando o secret do webhook.
Node.js
const crypto = require('crypto');
function verifyKortilinkWebhook(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}
// Express:
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-kortilink-signature'];
if (!verifyKortilinkWebhook(req.body, sig, process.env.KL_SECRET)) {
return res.status(401).send('invalid signature');
}
const event = JSON.parse(req.body.toString());
// ...processa
res.sendStatus(200);
});
Python
import hmac, hashlib
def verify_kortilink(raw_body, signature, secret):
expected = 'sha256=' + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
Códigos de erro
| Código | Significado |
|---|---|
400 | Body inválido / parâmetros em falta |
401 | API key inválida ou em falta |
403 | Plano insuficiente (Pro requerido) |
404 | Recurso não existe |
409 | Conflito (ex: custom_code já tomado) |
429 | Rate limit excedido |
500 | Erro interno (inclui reqId para suporte) |
Rate limits
| Endpoint | Limite |
|---|---|
POST /v1/links | 60 req/min |
Outros /v1/* | 20-30 req/min |
| Gestão de keys/webhooks (UI) | 5 req/min |
Excedido → 429 Too Many Requests. Espera 60s e tenta de novo.
Precisas de ajuda?
Bugs, dúvidas ou pedidos de novos endpoints — contacta suporte@kortilink.com ou abre uma issue.