Appearance
Guia para LLMs e agentes — API v1
Este documento é auto-contido: uma LLM ou agente consegue integrar a API v1 da FastGivr lendo apenas esta página. Tudo que está aqui é o contrato real da v1.
Use somente a API v1 (
/v1/...). Os endpoints em/access,/quickpay,/consolidation,/billing,/public,/enumsestão descontinuados e não devem ser usados em integrações novas.
1. Contrato mínimo
| Item | Valor |
|---|---|
| Base URL | https://api.fastgivr.com.br |
| Prefixo | /v1 (não existe prefixo /api) |
| Formato | JSON. Envie Content-Type: application/json e Accept: application/json |
| Autenticação | Authorization: Bearer <access_token> (JWT) |
| Header de contexto | X-Organization-Id: <uuid> — obrigatório em /v1/users/* e /v1/clients/* |
| Método de atualização | PUT ou PATCH (equivalentes) |
Envelope de resposta
Sucesso:
json
{ "code": 200, "success": true, "<chave>": { }, "message": "texto opcional" }<chave>varia por endpoint:user,users,organization,organizations,client,clients, oudata. Está indicada em cada endpoint abaixo.- Alguns endpoints (logout, delete, forgot-password) retornam só
code,successemessage.
Erro:
json
{ "code": 422, "success": false, "data": { "campo": ["mensagem"] }, "message": "Descrição do erro" }datatraz erros de validação por campo, ou uma string, ounull.- Sempre cheque
success(booleano) antes de usar o corpo.
Códigos HTTP
| Código | Significado | Ação da LLM |
|---|---|---|
200 | OK | usar data/<chave> |
201 | Criado | usar o recurso retornado |
401 | Token ausente/inválido/expirado | renovar via POST /v1/auth/refresh; se falhar, refazer login |
403 | Sem permissão | não repetir; reportar |
404 | Não encontrado ou fora da organização em contexto | verificar id e o header X-Organization-Id |
422 | Validação | ler data (erros por campo) e corrigir o corpo |
429 | Rate limit | aguardar e repetir com backoff |
500 | Erro interno | repetir 1x; se persistir, reportar |
2. Autenticação
Fluxo completo:
POST /v1/auth/register ──► { access_token } (novo usuário)
POST /v1/auth/login ──► { access_token } (usuário existente)
guarde o token e o expires_in
toda chamada protegida ──► header Authorization: Bearer <access_token>
antes de expires_in ──► POST /v1/auth/refresh (novo token)
qualquer 401 ──► refresh; se falhar, login de novo
ao encerrar ──► POST /v1/auth/logoutResposta de token (de register, login e refresh):
json
{
"code": 200,
"success": true,
"data": {
"access_token": "eyJhbGciOi...",
"token_type": "bearer",
"expires_in": 3600,
"user": { "id": 1, "name": "Ada", "email": "ada@ex.com", "phone": null, "email_verified_at": null }
}
}expires_in está em segundos. Agende o refresh para antes disso.
3. Contexto de organização
Um usuário pode pertencer a várias organizações (user.organizations[]).
/v1/organizations/*— a organização vem sempre na URL ({id}), não usa header./v1/users/*e/v1/clients/*— operam sobre a organização em contexto:- Se
X-Organization-Id: <uuid>for enviado com ouuidde uma organização do usuário → usa essa. - Senão → usa a organização de maior
iddo usuário. - Se o usuário não tiver nenhuma →
404.
- Se
Regra para a LLM: sempre envie X-Organization-Id nas chamadas a /v1/users/* e /v1/clients/*. Pegue o uuid em GET /v1/auth/me (data.user.organizations[].uuid) ou na resposta de POST /v1/organizations.
4. Modelos (TypeScript)
ts
type ISODateTime = string; // "2026-09-08T12:00:00.000000Z"
type ISODate = string; // "2026-09-08"
type OrganizationStatus =
| 'ACTIVE' | 'INACTIVE' | 'SUSPENDED' | 'DISABLED'
| 'DISQUALIFIED' | 'SOFT_DELETED' | 'DELETED';
interface Organization {
id: number;
uuid: string; // usar no header X-Organization-Id
name: string;
email: string | null;
document: string; // CPF ou CNPJ, único
status: OrganizationStatus; // criada como "INACTIVE"
created_by: number;
updated_by: number;
created_at: ISODateTime;
updated_at: ISODateTime;
deleted_at: ISODateTime | null;
}
interface User {
id: number;
name: string;
email: string;
phone: string | null;
email_verified_at: ISODateTime | null;
status: number | string | null;
created_at: ISODateTime;
updated_at: ISODateTime;
// password NUNCA é retornado
organizations?: Organization[]; // presente em GET /v1/auth/me
}
interface Client {
id: number;
code: string; // gerado pela API, único
name: string;
email: string | null;
phone: string | null;
document: string | null; // único por organização
type: 'individual' | 'company' | null;
gender: 'male' | 'female' | null;
birthdate: ISODate | null;
metadata: Record<string, unknown> | null;
status: string; // criado como "ACTIVE"
organization_id: number;
created_at: ISODateTime;
updated_at: ISODateTime;
}
type ApiSuccess<T, K extends string = 'data'> = {
code: number; success: true; message?: string;
} & { [P in K]: T };
type ApiError = {
code?: number; success: false;
data: Record<string, string[]> | string | null;
message: string;
};
type ApiResult<T, K extends string = 'data'> = ApiSuccess<T, K> | ApiError;5. Endpoints — request e response completos
Legenda: 🔓 público · 🔒 exige Authorization · 🏢 exige também X-Organization-Id.
5.1 Auth
POST /v1/auth/register 🔓
Request:
json
{ "name": "Ada Lovelace", "email": "ada@ex.com",
"password": "senhaForte8", "password_confirmation": "senhaForte8",
"phone": "+5511999999999" }Regras: name obrigatório ≤255 · email obrigatório, válido, único ≤255 · password obrigatório ≥8, igual a password_confirmation · phone opcional ≤20. Response 201, envelope de token (chave data). Dispara e-mail de verificação.
POST /v1/auth/login 🔓
Request: { "email": "ada@ex.com", "password": "senhaForte8" } Response 200, envelope de token (chave data). 401 message: "Credenciais inválidas." se errado.
GET /v1/auth/me 🔒
Request: sem corpo. Response 200, chave user:
json
{ "code": 200, "success": true,
"user": { "id": 1, "name": "Ada", "email": "ada@ex.com",
"organizations": [ { "id": 3, "uuid": "0f8f...", "name": "Acme", "status": "INACTIVE" } ] } }POST /v1/auth/logout 🔒
Sem corpo. Response 200 { "code":200,"success":true,"message":"Sessão encerrada com sucesso." }. O token é invalidado.
POST /v1/auth/refresh 🔒
Sem corpo. Response 200, novo envelope de token. 401 se não puder renovar.
POST /v1/auth/forgot-password 🔓
Request: { "email": "ada@ex.com" } (deve existir). Response 200message: "Código de recuperação enviado para o e-mail informado." Envia código de 6 dígitos (expira em 1 h).
POST /v1/auth/reset-password 🔓
Request:
json
{ "email": "ada@ex.com", "code": "123456",
"password": "novaSenha8", "password_confirmation": "novaSenha8" }code = 6 caracteres. Response 200 message: "Senha redefinida com sucesso." · 422 message: "Código de recuperação inválido ou expirado."
POST /v1/auth/verify-email 🔓
Request: { "email": "ada@ex.com", "code": "123456" }. Response 200message: "E-mail verificado com sucesso." · 422 se o código for inválido/expirado.
POST /v1/auth/send-verification-code 🔒
Sem corpo (usa o e-mail do usuário autenticado). Response 200message: "Código de verificação enviado com sucesso."
5.2 Organizations
GET /v1/organizations 🔒
Sem corpo. Response 200, chave organizations = Organization[] do usuário.
POST /v1/organizations 🔒
Request:
json
{ "name": "Acme Ltda", "email": "financeiro@acme.com", "document": "12345678000199" }Regras: name obrigatório ≤255 · email opcional, válido ≤255 · document obrigatório ≤20, único. Response 201, chave organization, message: "Organização criada com sucesso." O usuário é vinculado. status inicial = INACTIVE.
GET /v1/organizations/{id} 🔒
Response 200, chave organization. 404 se não existir ou não for do usuário.
PUT/PATCH /v1/organizations/{id} 🔒
Request (todos opcionais — envie só o que muda):
json
{ "name": "Acme S.A.", "email": "novo@acme.com",
"document": "12345678000199", "status": "ACTIVE" }status ∈ ACTIVE, INACTIVE, SUSPENDED, DISABLED, DISQUALIFIED, SOFT_DELETED, DELETED. document único (ignora o próprio). Response 200, chave organization, message: "Organização atualizada com sucesso."
DELETE /v1/organizations/{id} 🔒
Sem corpo. Response 200 message: "Organização removida com sucesso.".
5.3 Users (🏢 header X-Organization-Id)
GET /v1/users 🏢
Sem corpo. Response 200, chave users = User[] da organização em contexto.
POST /v1/users 🏢
Request:
json
{ "name": "Grace Hopper", "email": "grace@acme.com",
"password": "senhaForte8", "password_confirmation": "senhaForte8",
"phone": "+5511988887777" }Regras: name obrigatório ≤255 · email obrigatório, válido, único ≤255 · password obrigatório ≥8 + password_confirmation igual · phone opcional ≤20. Response 201, chave user, message: "Usuário criado com sucesso."
GET /v1/users/{id} 🏢
Response 200, chave user. 404 se o usuário não pertencer à organização em contexto.
PUT/PATCH /v1/users/{id} 🏢
Request (opcionais): name (≤255), email (válido, único, ignora o próprio), phone (≤20), password (≥8) + password_confirmation. Response 200, chave user, message: "Usuário atualizado com sucesso."
DELETE /v1/users/{id} 🏢
Sem corpo. Response 200 message: "Usuário removido com sucesso." (desvincula da organização e apaga).
5.4 Clients (🏢 header X-Organization-Id)
GET /v1/clients 🏢
Sem corpo. Response 200, chave clients = Client[] da organização (ordem id desc).
POST /v1/clients 🏢
Request:
json
{ "name": "João da Silva", "email": "joao@ex.com", "phone": "+5511970001122",
"document": "39053344705", "type": "individual", "gender": "male",
"birthdate": "1990-05-20", "metadata": { "origem": "site" } }Regras: name obrigatório ≤255 · email opcional válido ≤255 · phone opcional ≤20 · document opcional ≤20, único por organização · type ∈ individual,company · gender ∈ male,female · birthdate data YYYY-MM-DD · metadata objeto livre. Response 201, chave client, message: "Cliente criado com sucesso." status inicial = ACTIVE. 422 message: "Já existe um cliente com este documento nesta organização." em documento duplicado.
GET /v1/clients/{id} 🏢
Response 200, chave client. 404 se não pertencer à organização em contexto.
PUT/PATCH /v1/clients/{id} 🏢
Request (opcionais): mesmos campos do POST (name, email, phone, document, type, gender, birthdate, metadata). Response 200, chave client, message: "Cliente atualizado com sucesso."
DELETE /v1/clients/{id} 🏢
Sem corpo. Response 200 message: "Cliente removido com sucesso." (marca status = DELETED ).
6. Cliente TypeScript pronto
ts
const BASE_URL = 'https://api.fastgivr.com.br/v1';
type Session = { accessToken: string; expiresAt: number; organizationUuid?: string };
class FastGivrV1 {
private session: Session | null = null;
private async request<T>(
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
path: string,
body?: unknown,
orgUuid?: string,
): Promise<any> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
Accept: 'application/json',
};
if (this.session?.accessToken) headers.Authorization = `Bearer ${this.session.accessToken}`;
const org = orgUuid ?? this.session?.organizationUuid;
if (org) headers['X-Organization-Id'] = org;
let res = await fetch(`${BASE_URL}${path}`, {
method, headers, body: body ? JSON.stringify(body) : undefined,
});
if (res.status === 401 && this.session) {
await this.refresh().catch(() => (this.session = null));
if (this.session) {
headers.Authorization = `Bearer ${this.session.accessToken}`;
res = await fetch(`${BASE_URL}${path}`, {
method, headers, body: body ? JSON.stringify(body) : undefined,
});
}
}
const json = await res.json().catch(() => ({}));
if (!res.ok || json?.success === false) {
const err = new Error(json?.message ?? `HTTP ${res.status}`);
(err as any).status = res.status;
(err as any).fields = json?.data ?? null; // erros de validação por campo (422)
throw err;
}
return json;
}
private setSession(data: { access_token: string; expires_in: number }) {
this.session = {
accessToken: data.access_token,
expiresAt: Date.now() + data.expires_in * 1000,
organizationUuid: this.session?.organizationUuid,
};
}
async register(input: { name: string; email: string; password: string; phone?: string }) {
const r = await this.request('POST', '/auth/register', {
...input, password_confirmation: input.password,
});
this.setSession(r.data);
return r.data.user as any;
}
async login(email: string, password: string) {
const r = await this.request('POST', '/auth/login', { email, password });
this.setSession(r.data);
return r.data.user as any;
}
async refresh() {
const r = await this.request('POST', '/auth/refresh');
this.setSession(r.data);
}
async me() {
const r = await this.request('GET', '/auth/me');
return r.user as any; // inclui organizations[]
}
useOrganization(uuid: string) {
if (this.session) this.session.organizationUuid = uuid;
}
// Organizations
listOrganizations = () => this.request('GET', '/organizations').then((r) => r.organizations);
createOrganization = (b: { name: string; document: string; email?: string }) =>
this.request('POST', '/organizations', b).then((r) => r.organization);
getOrganization = (id: number) => this.request('GET', `/organizations/${id}`).then((r) => r.organization);
updateOrganization = (id: number, b: Record<string, unknown>) =>
this.request('PUT', `/organizations/${id}`, b).then((r) => r.organization);
deleteOrganization = (id: number) => this.request('DELETE', `/organizations/${id}`);
// Users (usa organizationUuid da sessão)
listUsers = () => this.request('GET', '/users').then((r) => r.users);
createUser = (b: { name: string; email: string; password: string; phone?: string }) =>
this.request('POST', '/users', { ...b, password_confirmation: b.password }).then((r) => r.user);
getUser = (id: number) => this.request('GET', `/users/${id}`).then((r) => r.user);
updateUser = (id: number, b: Record<string, unknown>) =>
this.request('PUT', `/users/${id}`, b).then((r) => r.user);
deleteUser = (id: number) => this.request('DELETE', `/users/${id}`);
// Clients
listClients = () => this.request('GET', '/clients').then((r) => r.clients);
createClient = (b: Record<string, unknown>) =>
this.request('POST', '/clients', b).then((r) => r.client);
getClient = (id: number) => this.request('GET', `/clients/${id}`).then((r) => r.client);
updateClient = (id: number, b: Record<string, unknown>) =>
this.request('PUT', `/clients/${id}`, b).then((r) => r.client);
deleteClient = (id: number) => this.request('DELETE', `/clients/${id}`);
}7. Receitas de integração
Cadastro + primeira organização + primeiro cliente
ts
const api = new FastGivrV1();
await api.register({ name: 'Ada', email: 'ada@ex.com', password: 'senhaForte8' });
const org = await api.createOrganization({ name: 'Acme', document: '12345678000199' });
api.useOrganization(org.uuid); // passa a enviar X-Organization-Id
const client = await api.createClient({
name: 'João', document: '39053344705', type: 'individual',
});Login e trabalho dentro de uma organização
ts
const api = new FastGivrV1();
await api.login('ada@ex.com', 'senhaForte8');
const me = await api.me();
api.useOrganization(me.organizations[0].uuid); // escolher a organização certa
const users = await api.listUsers();Tratar erro de validação (422)
ts
try {
await api.createUser({ name: 'X', email: 'invalido', password: '123' });
} catch (e: any) {
if (e.status === 422) {
// e.fields === { email: ["The email must be a valid email address."],
// password: ["The password must be at least 8 characters."] }
}
}8. Regras obrigatórias para a LLM
- Só use
/v1. Nunca chame/access,/quickpay,/consolidation,/billing,/public,/enums. - Sempre envie
Content-Type: application/jsoneAccept: application/json. - Em
POST /v1/auth/registere nos endpoints compassword, sempre incluapassword_confirmationcom o mesmo valor. Mínimo 8 caracteres. - Antes de qualquer chamada a
/v1/users/*ou/v1/clients/*, defina a organização e envieX-Organization-Id: <uuid>. - Ao receber
401, tentePOST /v1/auth/refreshuma vez; se falhar, refaça o login. - Ao receber
422, leiadata(erros por campo), corrija o corpo e repita — não repita a mesma requisição sem mudar nada. idde organização é numérico (URL);uuidé string (header). Não troque.- Atualizações são parciais: em
PUT/PATCHenvie apenas os campos que mudam. documentdeve ser um CPF (11 dígitos) ou CNPJ (14 dígitos) válido, sem pontuação é aceito. É único (organização, ou por organização no caso de cliente).- Nunca espere o campo
passwordem respostas — ele nunca é retornado.
9. Enums
OrganizationStatus (campo organization.status)
| Valor | Significado |
|---|---|
ACTIVE | Ativa e operante. |
INACTIVE | Inativa temporariamente (valor inicial). |
SUSPENDED | Suspensa por pendência/violação. |
DISABLED | Desabilitada por um administrador. |
DISQUALIFIED | Desqualificada, impedida de usar o sistema. |
SOFT_DELETED | Marcada para exclusão. |
DELETED | Removida permanentemente. |
ClientStatus (campo client.status)
| Valor | Nome |
|---|---|
2 | ACTIVE (valor inicial ao criar) |
1 | STATUS |
-1 | DELETED (após DELETE) |
10. Índice rápido (para parsing)
GET /v1/organizations auth -> { organizations: Organization[] }
POST /v1/organizations auth body{name,document,email?} -> { organization }
GET /v1/organizations/{id} auth -> { organization }
PUT /v1/organizations/{id} auth body{name?,email?,document?,status?} -> { organization }
DELETE /v1/organizations/{id} auth -> { message }
GET /v1/users auth + org -> { users: User[] }
POST /v1/users auth + org body{name,email,password,password_confirmation,phone?} -> { user }
GET /v1/users/{id} auth + org -> { user }
PUT /v1/users/{id} auth + org body{name?,email?,phone?,password?,password_confirmation?} -> { user }
DELETE /v1/users/{id} auth + org -> { message }
GET /v1/clients auth + org -> { clients: Client[] }
POST /v1/clients auth + org body{name,email?,phone?,document?,type?,gender?,birthdate?,metadata?} -> { client }
GET /v1/clients/{id} auth + org -> { client }
PUT /v1/clients/{id} auth + org body{...mesmos do POST} -> { client }
DELETE /v1/clients/{id} auth + org -> { message }
POST /v1/auth/register public body{name,email,password,password_confirmation,phone?} -> { data: TokenResponse }
POST /v1/auth/login public body{email,password} -> { data: TokenResponse }
GET /v1/auth/me auth -> { user: User & { organizations: Organization[] } }
POST /v1/auth/logout auth -> { message }
POST /v1/auth/refresh auth -> { data: TokenResponse }
POST /v1/auth/forgot-password public body{email} -> { message }
POST /v1/auth/reset-password public body{email,code,password,password_confirmation} -> { message }
POST /v1/auth/verify-email public body{email,code} -> { message }
POST /v1/auth/send-verification-code auth -> { message }