Skip to content

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, /enums estão descontinuados e não devem ser usados em integrações novas.


1. Contrato mínimo

ItemValor
Base URLhttps://api.fastgivr.com.br
Prefixo/v1 (não existe prefixo /api)
FormatoJSON. Envie Content-Type: application/json e Accept: application/json
AutenticaçãoAuthorization: Bearer <access_token> (JWT)
Header de contextoX-Organization-Id: <uuid>obrigatório em /v1/users/* e /v1/clients/*
Método de atualizaçãoPUT 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, ou data. Está indicada em cada endpoint abaixo.
  • Alguns endpoints (logout, delete, forgot-password) retornam só code, success e message.

Erro:

json
{ "code": 422, "success": false, "data": { "campo": ["mensagem"] }, "message": "Descrição do erro" }
  • data traz erros de validação por campo, ou uma string, ou null.
  • Sempre cheque success (booleano) antes de usar o corpo.

Códigos HTTP

CódigoSignificadoAção da LLM
200OKusar data/<chave>
201Criadousar o recurso retornado
401Token ausente/inválido/expiradorenovar via POST /v1/auth/refresh; se falhar, refazer login
403Sem permissãonão repetir; reportar
404Não encontrado ou fora da organização em contextoverificar id e o header X-Organization-Id
422Validaçãoler data (erros por campo) e corrigir o corpo
429Rate limitaguardar e repetir com backoff
500Erro internorepetir 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/logout

Resposta 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:
    1. Se X-Organization-Id: <uuid> for enviado com o uuid de uma organização do usuário → usa essa.
    2. Senão → usa a organização de maior id do usuário.
    3. Se o usuário não tiver nenhuma → 404.

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" }

statusACTIVE, 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 · typeindividual,company · gendermale,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

  1. Só use /v1. Nunca chame /access, /quickpay, /consolidation, /billing, /public, /enums.
  2. Sempre envie Content-Type: application/json e Accept: application/json.
  3. Em POST /v1/auth/register e nos endpoints com password, sempre inclua password_confirmation com o mesmo valor. Mínimo 8 caracteres.
  4. Antes de qualquer chamada a /v1/users/* ou /v1/clients/*, defina a organização e envie X-Organization-Id: <uuid>.
  5. Ao receber 401, tente POST /v1/auth/refresh uma vez; se falhar, refaça o login.
  6. Ao receber 422, leia data (erros por campo), corrija o corpo e repita — não repita a mesma requisição sem mudar nada.
  7. id de organização é numérico (URL); uuid é string (header). Não troque.
  8. Atualizações são parciais: em PUT/PATCH envie apenas os campos que mudam.
  9. document deve 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).
  10. Nunca espere o campo password em respostas — ele nunca é retornado.

9. Enums

OrganizationStatus (campo organization.status)

ValorSignificado
ACTIVEAtiva e operante.
INACTIVEInativa temporariamente (valor inicial).
SUSPENDEDSuspensa por pendência/violação.
DISABLEDDesabilitada por um administrador.
DISQUALIFIEDDesqualificada, impedida de usar o sistema.
SOFT_DELETEDMarcada para exclusão.
DELETEDRemovida permanentemente.

ClientStatus (campo client.status)

ValorNome
2ACTIVE (valor inicial ao criar)
1STATUS
-1DELETED (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 }