Documentação da API
API RESTful para integrar o FluxQR nas suas aplicações.
/api/v1Introdução
A API do FluxQR permite criar e gerir QR codes dinâmicos, consultar analytics e gerir a sua conta de forma programática. Todas as respostas são em formato JSON.
Base URL
https://fluxqr.com/api/v1Headers obrigatórios
Content-Type: application/json
Accept: application/json
Authorization: Bearer <your-api-token>Pode gerar tokens de API em Definições → API Tokens.
Autenticação
A API usa tokens Bearer via Laravel Sanctum. Pode obter um token via login ou criando um na interface web.
/api/v1/auth/registerCriar nova conta
/api/v1/auth/loginObter token de autenticação
/api/v1/auth/logoutRevogar o token atual
/api/v1/auth/userObter utilizador autenticado
Registar
Cria uma nova conta e retorna um token de acesso.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Sim | Nome do utilizador |
| string | Sim | Email (único) | |
| password | string | Sim | Password (mín. 8 caracteres) |
| password_confirmation | string | Sim | Confirmação da password |
| device_name | string | Não | Nome do dispositivo (default: "api") |
curl -X POST /api/v1/auth/register \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "João Silva",
"email": "joao@example.com",
"password": "password123",
"password_confirmation": "password123"
}'Resposta (201):
{
"user": {
"id": 1,
"name": "João Silva",
"email": "joao@example.com",
"locale": null,
"timezone": null,
"email_verified_at": null,
"two_factor_enabled": false,
"created_at": "2026-03-16T12:00:00+00:00"
},
"token": "1|abc123def456..."
}Login
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| string | Sim | Email do utilizador | |
| password | string | Sim | Password |
| device_name | string | Não | Nome do dispositivo |
curl -X POST /api/v1/auth/login \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"email": "joao@example.com",
"password": "password123"
}'Resposta (200):
{
"user": { "id": 1, "name": "João Silva", ... },
"token": "2|xyz789..."
}Logout
Revoga o token usado no pedido.
curl -X POST /api/v1/auth/logout \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"QR Codes
CRUD completo para gerir QR codes dinâmicos. Todos os endpoints requerem autenticação.
/api/v1/qr-codesListar todos os QR codes
/api/v1/qr-codesCriar novo QR code
/api/v1/qr-codes/{id}Ver detalhe de um QR code
/api/v1/qr-codes/{id}Atualizar QR code
/api/v1/qr-codes/{id}Eliminar QR code
/api/v1/qr-codes/{id}/toggle-statusAlternar status (ativo/pausado)
Listar QR Codes
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| search | string | Não | Pesquisar por nome, URL ou short code |
| status | string | Não | Filtrar por status: active, paused, archived |
| per_page | integer | Não | Itens por página (default: 15) |
| page | integer | Não | Número da página |
curl -X GET "/api/v1/qr-codes?status=active&per_page=10" \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Resposta (200):
{
"data": [
{
"id": 1,
"name": "Campanha Verão",
"short_code": "aBcDeFg",
"destination_url": "https://example.com/summer",
"redirect_url": "https://fluxqr.com/r/aBcDeFg",
"redirect_mode": 302,
"status": "active",
"expires_at": null,
"scans_count": 142,
"created_at": "2026-03-10T10:00:00+00:00",
"updated_at": "2026-03-15T14:30:00+00:00"
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": "..." },
"meta": { "current_page": 1, "last_page": 3, "per_page": 10, "total": 25 }
}Criar QR Code
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Sim | Nome do QR code (máx. 255) |
| destination_url | string | Sim | URL de destino (máx. 2048) |
curl -X POST /api/v1/qr-codes \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "Menu Restaurante",
"destination_url": "https://meurestaurante.pt/menu"
}'Resposta (201):
{
"data": {
"id": 5,
"name": "Menu Restaurante",
"short_code": "xYz1234",
"destination_url": "https://meurestaurante.pt/menu",
"redirect_url": "https://fluxqr.com/r/xYz1234",
"redirect_mode": 302,
"status": "active",
"expires_at": null,
"created_at": "2026-03-16T15:00:00+00:00",
"updated_at": "2026-03-16T15:00:00+00:00"
}
}Atualizar QR Code
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Sim | Novo nome |
| destination_url | string | Sim | Novo URL de destino (apenas planos pagos) |
Nota: No plano Free, a edição do destination_url é ignorada. Faça upgrade para alterar URLs.
curl -X PUT /api/v1/qr-codes/5 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "Menu Atualizado",
"destination_url": "https://meurestaurante.pt/menu-novo"
}'Eliminar QR Code
curl -X DELETE /api/v1/qr-codes/5 \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Resposta (200):
{ "message": "QR code deleted." }Alternar Status
Alterna entre active e paused.
curl -X PATCH /api/v1/qr-codes/5/toggle-status \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Analytics
Consultar métricas, séries temporais e scans detalhados.
/api/v1/analytics/dashboardMétricas globais do workspace
/api/v1/analytics/qr-codes/{id}Métricas de um QR code específico
/api/v1/analytics/qr-codes/{id}/scansListar scans de um QR code (paginado)
Dashboard
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| days | integer | Não | Período em dias para time series (default: 30) |
| limit | integer | Não | Limite de resultados para top/recent (default: 5) |
curl -X GET "/api/v1/analytics/dashboard?days=7" \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Resposta (200):
{
"metrics": {
"total_scans": 12450,
"scans_today": 85,
"scans_today_change": 12.5,
"scans_7_days": 520,
"scans_7_days_change": -3.2,
"scans_30_days": 2100,
"scans_30_days_change": 15.0,
"total_qr_codes": 45,
"active_qr_codes": 38
},
"time_series": [
{ "date": "2026-03-10", "count": 72 },
{ "date": "2026-03-11", "count": 95 }
],
"top_qr_codes": [
{ "id": 1, "name": "Campanha Verão", "short_code": "aBcDeFg", "scan_count": 142 }
],
"recent_qr_codes": [
{ "id": 5, "name": "Menu", "short_code": "xYz1234", "destination_url": "...", "status": "active", "scans_count": 10 }
]
}Analytics de QR Code
curl -X GET /api/v1/analytics/qr-codes/1 \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Resposta (200):
{
"qr_code": { "id": 1, "name": "Campanha Verão", "short_code": "aBcDeFg" },
"metrics": {
"total_scans": 142,
"scans_today": 5,
"scans_7_days": 38,
"scans_30_days": 120
},
"time_series": [ { "date": "2026-03-16", "count": 5 } ],
"recent_scans": [
{
"id": 501,
"scanned_at": "2026-03-16T14:32:00+00:00",
"device_type": "mobile",
"referrer": "instagram.com",
"country": "PT",
"is_bot": false
}
]
}Listar Scans
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| per_page | integer | Não | Itens por página (default: 25) |
| page | integer | Não | Número da página |
curl -X GET "/api/v1/analytics/qr-codes/1/scans?per_page=10" \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Resposta (200):
{
"data": [
{
"id": 501,
"qr_code_id": 1,
"scanned_at": "2026-03-16T14:32:00+00:00",
"device_type": "mobile",
"os": "iOS",
"browser": "Safari",
"country": "PT",
"region": "Lisboa",
"city": "Lisboa",
"language": "pt-PT",
"referrer": "instagram.com",
"is_bot": false
}
],
"links": { ... },
"meta": { "current_page": 1, "total": 142, ... }
}Billing
Consultar planos, limites de utilização e gerir subscrição.
/api/v1/plansListar planos disponíveis
/api/v1/billingPlano atual e limites de utilização
/api/v1/billing/upgradeFazer upgrade para Pro
/api/v1/billing/downgradeFazer downgrade para Free
Listar Planos
curl -X GET /api/v1/plans \
-H "Accept: application/json"Resposta (200):
{
"plans": [
{
"id": 1,
"name": "Free",
"slug": "free",
"is_free": true,
"limits": {
"max_qrcodes": 3,
"max_scans_monthly": 1000,
"max_custom_domains": 0,
"max_api_keys": 1,
"max_members": 1,
"features": []
}
},
{
"id": 2,
"name": "Pro",
"slug": "pro",
"is_free": false,
"limits": { "max_qrcodes": 50, "max_scans_monthly": 100000, ... }
}
]
}Plano Atual & Utilização
curl -X GET /api/v1/billing \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Resposta (200):
{
"limits": {
"plan_name": "Free",
"plan_slug": "free",
"max_qrcodes": 3,
"max_scans_monthly": 1000,
"used_qrcodes": 2,
"used_scans": 150,
"remaining_qrcodes": 1,
"remaining_scans": 850
}
}Upgrade / Downgrade
# Upgrade para Pro
curl -X POST /api/v1/billing/upgrade \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"
# Downgrade para Free
curl -X POST /api/v1/billing/downgrade \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"Perfil
Gerir dados do utilizador autenticado.
/api/v1/profileVer perfil
/api/v1/profileAtualizar perfil
/api/v1/profile/passwordAlterar password
/api/v1/profileEliminar conta
Atualizar Perfil
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Não | Nome (máx. 255) |
| string | Não | Email (único) | |
| locale | string | Não | Idioma: pt ou en |
| timezone | string | Não | Timezone (ex: Europe/Lisbon) |
curl -X PATCH /api/v1/profile \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{ "name": "João Silva", "locale": "pt" }'Alterar Password
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| current_password | string | Sim | Password atual |
| password | string | Sim | Nova password (mín. 8 caracteres) |
| password_confirmation | string | Sim | Confirmação da nova password |
curl -X PUT /api/v1/profile/password \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"current_password": "oldpassword",
"password": "newpassword123",
"password_confirmation": "newpassword123"
}'Eliminar Conta
Atenção: Esta ação é irreversível. Todos os dados, QR codes e tokens serão eliminados.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| password | string | Sim | Password atual para confirmação |
curl -X DELETE /api/v1/profile \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{ "password": "yourpassword" }'Erros
A API utiliza códigos de estado HTTP convencionais. Respostas de erro incluem sempre um campo message.
| Código | Significado | Descrição |
|---|---|---|
| 200 | OK | Pedido bem-sucedido |
| 201 | Created | Recurso criado com sucesso |
| 401 | Unauthorized | Token inválido ou ausente |
| 403 | Forbidden | Sem permissão para este recurso |
| 404 | Not Found | Recurso não encontrado |
| 422 | Unprocessable | Erro de validação ou limite atingido |
| 429 | Too Many Requests | Rate limit excedido |
| 500 | Server Error | Erro interno do servidor |
Formato de erro de validação (422)
{
"message": "The name field is required.",
"errors": {
"name": ["The name field is required."],
"destination_url": ["The destination url must be a valid URL."]
}
}Rate Limits
A API aplica limites de taxa para garantir a estabilidade do serviço.
| Contexto | Limite |
|---|---|
| API geral (autenticado) | 60 pedidos / minuto |
| Login / Registo | 5 pedidos / minuto |
| Redirect público (/r/) | 60 pedidos / minuto |
Quando o limite é excedido, receberá uma resposta 429 Too Many Requests. Aguarde o tempo indicado no header Retry-After.