API do Selfcare

Liga o Selfcare ao teu ERP, ao software de ponto, ao BI, a uma app móvel ou a um assistente de IA. A API age em nome de uma pessoa e vê exatamente o que ela vê na app.

URL base: https://api.selfcare.work/v1Especificação OpenAPI 3.1
URL base
https://api.selfcare.work/v1

Começar

1. Na app, abre Definições → API e cria uma chave com os âmbitos de que precisas (pedimos um código por email). O segredo aparece uma só vez: guarda-o num cofre.

2. Faz o primeiro pedido. Todas as rotas vivem em https://api.selfcare.work/v1 e respondem em JSON com data, error e, nas listas, meta.

curl
curl https://api.selfcare.work/v1/me \
  -H "Authorization: Bearer sc_live_…"

3. Em TypeScript, o SDK (@selfcare/api-sdk, gerado da especificação OpenAPI) trata da autenticação, das repetições e da paginação. O pacote ainda não está publicado no npm; até lá, usa a especificação OpenAPI com o gerador da tua preferência.

Nunca sai pela API: o processamento salarial, os atestados e outros documentos de saúde, os documentos disciplinares e o IBAN completo (sempre mascarado).

TypeScript
import { SelfcareClient } from "@selfcare/api-sdk";

const api = new SelfcareClient({ token: process.env.SELFCARE_API_KEY! });

const me = await api.getMe();
console.log(me.tenant.name, me.role, me.credential.scopes);

// Every approved absence this month, page after page
for await (const a of api.paginate((cursor) =>
  api.listAbsences({ cursor, query: { from: "2026-11-01", to: "2026-11-30", status: "APPROVED" } }),
)) {
  console.log(a.employee.fullName, a.startDate, a.endDate);
}

Chaves de API

Cada pedido leva Authorization: Bearer seguido da chave (sc_live_… nas empresas reais, sc_test_… na empresa de teste). A chave pertence a uma pessoa numa empresa: a empresa vem sempre da chave e o papel recalcula-se em cada pedido, por isso a chave vê o que a pessoa vê hoje na app.

Mudar ou repor a palavra-passe revoga as chaves dessa pessoa. O dono e o RH veem e revogam as chaves de toda a empresa em Definições → Sistema → API da empresa, e podem bloquear a API inteira numa emergência. O cookie da app nunca é aceite. Para integrações de servidor sem pessoa associada, o dono ou o RH criam um utilizador de integração, que não entra na app.

OAuth 2.1

Para uma app usada por várias pessoas (uma app móvel, um assistente, um produto teu), usa OAuth 2.1 em vez de chaves: cada pessoa entra com a sua conta, escolhe a empresa e vê o que a app pede. Regista a app em Definições → API → As minhas apps. Uma app nativa ou de browser é pública (sem segredo); um servidor é confidencial e autentica-se com client_secret, mostrado só ao criar ou ao rodar.

Só há authorization code com PKCE S256. O state é obrigatório e o redirect_uri tem de ser exatamente um dos registados: um esquema próprio com ponto (work.selfcare.app:/oauth), https, ou loopback por IP (http://127.0.0.1/callback, com qualquer porta, RFC 8252). Os domínios do Selfcare (selfcare.work e subdomínios) não servem de redirect. Cada resposta ao teu redirect, com código ou com erro, traz iss=https://api.selfcare.work (RFC 9207): confirma-o junto com o state antes de trocar o código. Os metadados estão em /.well-known/oauth-authorization-server.

HTTP
GET https://app.selfcare.work/oauth/authorize?response_type=code
  &client_id=sc_app_…&redirect_uri=work.selfcare.app:/oauth
  &scope=me:read%20absences:read%20absences:write&state=…
  &code_challenge=BASE64URL(SHA256(code_verifier))&code_challenge_method=S256

POST https://api.selfcare.work/oauth/token
  grant_type=authorization_code&client_id=sc_app_…&code=sc_ac_…
  &redirect_uri=work.selfcare.app:/oauth&code_verifier=…

POST https://api.selfcare.work/oauth/token
  grant_type=refresh_token&client_id=sc_app_…&refresh_token=sc_rt_…

O access_token (sc_at_…) dura 1 hora e usa-se como Authorization: Bearer, tal como uma chave. O refresh_token (sc_rt_…) dura 90 dias e roda em cada uso: guarda sempre o novo e descarta o antigo. Reutilizar um refresh token já usado revoga a sessão inteira, porque só acontece se alguém o copiou.

A app age sempre como a pessoa: o acesso efetivo é o âmbito concedido cruzado com o que o papel dela permite na empresa. Para terminar a sessão, chama POST /oauth/revoke com o refresh token. A pessoa revoga a app em Definições → API → Apps autorizadas, e o dono e o RH veem as apps autorizadas na empresa.

TypeScript
import { OAuthSession, SelfcareClient, buildAuthorizeUrl, createPkcePair, exchangeCode, parseAuthorizationResponse, randomString } from "@selfcare/api-sdk";

const clientId = "sc_app_…";
const redirectUri = "work.selfcare.app:/oauth";
const { verifier, challenge } = await createPkcePair();
const state = randomString();
openSystemBrowser(buildAuthorizeUrl({ clientId, redirectUri, scopes: ["me:read", "absences:write"], state, codeChallenge: challenge }));

// On the redirect: check "state" and "iss" (RFC 9207), then exchange the code
const { code } = parseAuthorizationResponse(redirectedUrl, { state, issuer: "https://api.selfcare.work" });
const tokens = await exchangeCode({ clientId, code, redirectUri, codeVerifier: verifier });
const session = new OAuthSession(tokens, { clientId }, (t) => saveToKeychain(t)); // t === null: signed out
const api = new SelfcareClient({ token: () => session.accessToken(), onUnauthorized: () => session.forceRefresh() });

Âmbitos

O acesso é a interseção entre os âmbitos da credencial e as permissões da pessoa na app: um âmbito nunca dá mais do que o papel. Os âmbitos sensíveis (contratos, remuneração, IBAN) marcam-se um a um, nunca entram nos atalhos, e uma chave com eles vale no máximo 90 dias. As escritas de contratos e remuneração são só para o dono e o RH.

ÂmbitoO que permite
me:readQuem sou: utilizador, empresa e papel
absences:readVer ausências, férias e saldos
absences:writeCriar, editar e cancelar ausências
absences:decideAprovar e rejeitar ausências
expenses:readVer despesas e anexos
expenses:writeCriar, editar e submeter despesas
expenses:decideAprovar e rejeitar despesas
trips:readVer deslocações (ajudas de custo)
trips:writeRegistar e cancelar deslocações
trips:decideAprovar e rejeitar deslocações
documents:readVer e descarregar documentos (nunca os de saúde)
documents:writeCarregar documentos e recibos
employees:readVer colaboradores
employees:writeCriar e editar colaboradores
teams:readVer equipas
teams:writeGerir equipas
webhooks:manageGerir webhooks
contracts:readsensívelVer contratos
contracts:writesensívelAlterar contratos
compensation:readsensívelVer remunerações
compensation:writesensívelAlterar remunerações
bank-details:writesensívelPedir mudanças de IBAN

Paginação

As listas devolvem meta.nextCursor e meta.hasMore. Para a página seguinte, repete o pedido com cursor igual ao nextCursor e os mesmos filtros (um cursor com outros filtros dá 400 INVALID_CURSOR). limit vale 50 por omissão e vai até 200. Para sincronizar só o que mudou, usa updatedSince.

JSON
{
  "data": [ … ],
  "error": null,
  "meta": { "nextCursor": "eyJj…", "hasMore": true }
}

Erros

Os erros vêm em error, com um code estável, a mensagem na língua do Accept-Language (PT por omissão, ou EN), details por campo nos erros de validação, o requestId (também no cabeçalho Selfcare-Request-Id) e o docUrl desta página. Um recurso que não podes ver responde 404, nunca 403, para não revelar que existe.

CódigoEstadoMensagem
BAD_REQUEST400O pedido está mal formado.
INVALID_CURSOR400O cursor de paginação é inválido ou não corresponde a estes filtros.
UNAUTHORIZED401É preciso uma chave de API ou um token válido no cabeçalho Authorization: Bearer.
PAYMENT_REQUIRED402A conta da empresa está bloqueada por falta de pagamento.
FORBIDDEN403Não tens permissão para fazer isto.
SCOPE_MISSING403A credencial não tem o âmbito necessário: ….
API_BLOCKED403A API está bloqueada nesta empresa.
NOT_FOUND404Não encontrado.
CONFLICT409O recurso está num estado que não permite esta operação, ou um pedido igual ainda está a correr.
CONFIRMATION_PENDING409A alteração fica pendente até o colaborador a confirmar na app.
PRECONDITION_FAILED412O recurso mudou desde que o leste (If-Match não corresponde ao ETag atual). Lê-o de novo.
PRECONDITION_REQUIRED428Esta escrita exige o cabeçalho indicado nos detalhes (If-Match com o ETag atual, ou Idempotency-Key).
VALIDATION_ERROR422Há campos inválidos no pedido.
IDEMPOTENCY_CONFLICT422Esta Idempotency-Key já foi usada com um pedido diferente.
CHECKSUM_MISMATCH422O sha256 do ficheiro recebido não corresponde ao declarado.
RATE_LIMITED429Demasiados pedidos. Tenta de novo daqui a pouco.
INTERNAL_ERROR500Ocorreu um erro inesperado.
SERVICE_UNAVAILABLE503Serviço temporariamente indisponível.
HTTP
HTTP/1.1 403 Forbidden
Selfcare-Request-Id: 6f0e…

{
  "data": null,
  "error": {
    "code": "SCOPE_MISSING",
    "message": "A credencial não tem o âmbito necessário: absences:write.",
    "requestId": "6f0e…",
    "docUrl": "https://api.selfcare.work/docs#error-scope-missing"
  }
}

Limites

Por credencial: 300 pedidos por minuto, dos quais 60 escritas. Por empresa, somando todas as credenciais: 1200 por minuto. Por app OAuth, em todas as empresas: 3000 por minuto. Antes de validar a credencial, há ainda 600 pedidos por minuto por IP. As respostas trazem RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset; um 429 traz Retry-After em segundos. Espera esse tempo antes de repetir.

Idempotency-Key

Qualquer escrita (POST, PATCH, PUT, DELETE) aceita Idempotency-Key: um valor teu, único por operação (um UUID serve). Durante 24 horas, repetir o pedido com a mesma chave e o mesmo corpo devolve a mesma resposta sem repetir a ação, com o cabeçalho Idempotent-Replayed: true. A mesma chave com outro corpo dá 422 IDEMPOTENCY_CONFLICT; um pedido igual ainda a correr dá 409 CONFLICT. Nas escritas sensíveis (contratos e remuneração), a chave é obrigatória (428).

curl
curl -X POST https://api.selfcare.work/v1/absences \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{ "absenceTypeCode": "FERIAS", "startDate": "2026-12-21", "endDate": "2026-12-24" }'

# Same key, same body, within 24 h: the same answer, with "Idempotent-Replayed: true".
# Same key, another body: 422 IDEMPOTENCY_CONFLICT. Still running: 409 CONFLICT.

Para editar sem pisar uma alteração de outra pessoa, usa o ETag que vem nas leituras e nas escritas: manda-o em If-Match. Se o recurso mudou entretanto, a resposta é 412 PRECONDITION_FAILED. Nas edições sensíveis, If-Match é obrigatório.

curl
curl -i https://api.selfcare.work/v1/absences/3f1c…   # ETag: W/"…"
curl -X PATCH https://api.selfcare.work/v1/absences/3f1c… \
  -H "Authorization: Bearer sc_live_…" -H "If-Match: W/\"…\"" \
  -H "Content-Type: application/json" -d '{ "endDate": "2026-12-23" }'
# If the absence changed in the meantime: 412 PRECONDITION_FAILED

Empresa de teste

Para desenvolver sem tocar em dados reais, cria uma empresa de teste em Definições → API: vem com colaboradores, duas equipas, ausências e despesas fictícios. Nessa empresa as chaves começam por sc_test_ e só funcionam lá; as sc_live_ só funcionam nas empresas reais. Os tokens OAuth servem a empresa escolhida no consentimento, seja de teste ou real.

Versões

A versão vai no caminho (/v1). Dentro de /v1 só entram mudanças compatíveis: campos, rotas, eventos e valores novos. Ignora os campos que não conheces e trata valores de enumeração novos com um caso por omissão. Uma mudança incompatível só numa /v2, anunciada com antecedência nesta página.

MCP (assistentes de IA)

O servidor MCP liga o Selfcare a assistentes de IA como o Claude, o ChatGPT ou o Cursor. Cada pessoa entra com a sua conta e o assistente passa a poder marcar férias, registar despesas, ver documentos e tratar aprovações, sempre a agir como ela e a ver só o que ela vê na app. As ferramentas usam os mesmos endpoints da API, com os mesmos âmbitos, limites e registo de auditoria (com a origem MCP).

Claude

1. Em Definições → Conectores, escolhe Adicionar conector personalizado.

2. Dá-lhe um nome (Selfcare) e cola o URL acima.

3. Carrega em Ligar: abre o ecrã de autorização do Selfcare. Entra, escolhe a empresa e confirma o que o assistente pode fazer.

ChatGPT

1. Em Definições → Apps e conectores, ativa o modo de programador (nas definições avançadas) e escolhe Criar.

2. Cola o URL acima e escolhe OAuth como autenticação.

3. Confirma no ecrã de autorização do Selfcare, como no Claude.

URL
https://api.selfcare.work/mcp

Cursor

1. Abre as definições do Cursor, secção MCP, e acrescenta o servidor ao mcp.json (do projeto ou global).

2. Na primeira utilização, o Cursor abre o browser para entrares no Selfcare e autorizares.

mcp.json
{
  "mcpServers": {
    "selfcare": { "url": "https://api.selfcare.work/mcp" }
  }
}

Clientes que não fazem OAuth podem usar uma chave de API pessoal no cabeçalho Authorization. Cria-a com os âmbitos das ferramentas que vais usar:

mcp.json
{
  "mcpServers": {
    "selfcare": {
      "url": "https://api.selfcare.work/mcp",
      "headers": { "Authorization": "Bearer sc_live_…" }
    }
  }
}

Nos três casos, o cliente regista-se sozinho (registo dinâmico, RFC 7591) e o Selfcare mostra um aviso de que se trata de um assistente de IA. Transporte Streamable HTTP sem sessão. Sem token, a resposta é 401 com WWW-Authenticate e resource_metadata (RFC 9728); os tokens são emitidos para o recurso https://api.selfcare.work/mcp (RFC 8707) e não servem na API REST, nem o contrário. Ferramentas:

FerramentaÂmbitoTipo
get_meme:readLeitura
get_my_summaryme:readLeitura
get_vacation_balanceabsences:readLeitura
list_absence_typesabsences:readLeitura
list_absencesabsences:readLeitura
request_absenceabsences:writeEscrita
cancel_absenceabsences:writeEscrita
get_absenceabsences:readLeitura
list_pending_approvalsabsences:readLeitura
approve_absenceabsences:decideEscrita
reject_absenceabsences:decideEscrita
list_expensesexpenses:readLeitura
get_expenseexpenses:readLeitura
list_expense_categoriesexpenses:readLeitura
create_expenseexpenses:writeEscrita
get_expense_receipt_upload_urlexpenses:writeEscrita
add_expense_receiptexpenses:writeEscrita
submit_expenseexpenses:writeEscrita
approve_expenseexpenses:decideEscrita
reject_expenseexpenses:decideEscrita
list_documentsdocuments:readLeitura
get_document_linkdocuments:readLeitura
search_employeesemployees:readLeitura

Dos documentos, o assistente só vê o regulamento interno, as políticas e os manuais da empresa e os comprovativos de despesa da própria pessoa, cada um na sua pasta. Nunca vê recibos de vencimento, recibos de ajudas de custo, tipos de documento criados pela empresa, contratos e adendas, documentos de identificação (cartão de cidadão, passaporte, autorização de residência), comprovativos de NIF, NISS ou IBAN, documentos de saúde ou disciplinares, atestados, o IBAN completo (sempre mascarado) nem dados de processamento salarial, e não está disponível para contabilistas. Cada escrita fica no registo de auditoria da empresa com a origem MCP e a app. A pessoa revoga o acesso em Definições → API → Apps autorizadas, e os clientes registados que ninguém autoriza em 7 dias são apagados.

Webhooks

Os webhooks avisam um servidor teu quando algo muda: ausências, despesas, deslocações, documentos, pessoas e equipas. Cada endpoint é de uma pessoa e só recebe o que ela vê no momento da entrega. Cria-os em Definições → API → Webhooks ou pela API (POST /v1/webhook-endpoints, âmbito webhooks:manage), até 10 por pessoa, só em https público. O segredo (whsec_…) aparece ao criar e ao rodar.

Cada entrega é um POST JSON com o id do evento, o tipo, a empresa e os ids do recurso, da ficha e da pessoa, com o estado e as datas. Nunca leva o IBAN, motivos, tipos de ausência, nomes de documentos nem valores: lê o resto pela API, com as permissões de quem lê.

HTTP
POST https://erp.example.pt/selfcare/webhooks
Selfcare-Signature: t=1760000000,v1=5257a869…
Selfcare-Event-Id: 0b8e…
Selfcare-Event-Type: absence.approved
Selfcare-Delivery-Attempt: 1

{
  "id": "0b8e…",
  "type": "absence.approved",
  "apiVersion": "v1",
  "createdAt": "2026-11-03T09:12:00.000Z",
  "occurredAt": "2026-11-03T09:12:00.000Z",
  "tenantId": "aaaa…",
  "data": { "object": "absence", "id": "3f1c…", "employeeId": "…", "userId": "…", "status": "APPROVED" }
}

Verifica sempre a assinatura antes de confiar no corpo. Selfcare-Signature traz t= (o instante em segundos) e um ou mais v1= (o HMAC-SHA256 em hex) do segredo sobre t.corpo (o corpo tal como chegou). Recusa assinaturas com mais de 5 minutos e deduplica pelo id do evento: a entrega é pelo menos uma vez, sem ordem garantida.

TypeScript
import { verifyWebhookSignature } from "@selfcare/api-sdk";

export async function POST(req: Request) {
  const body = await req.text(); // the raw body, before JSON.parse
  const ok = await verifyWebhookSignature({
    header: req.headers.get("Selfcare-Signature"),
    body,
    secret: process.env.SELFCARE_WEBHOOK_SECRET!, // whsec_…
  });
  if (!ok) return new Response("bad signature", { status: 400 });
  const event = JSON.parse(body);
  if (await alreadyProcessed(event.id)) return new Response(null, { status: 200 }); // at least once
  await handle(event); // then read the resource through the API
  return new Response(null, { status: 200 });
}

Sem o SDK, em Node:

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(header: string | null, body: string, secret: string): boolean {
  if (!header) return false;
  const parts = header.split(",").map((p) => p.trim());
  const t = Number(parts.find((p) => p.startsWith("t="))?.slice(2));
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest();
  return parts
    .filter((p) => p.startsWith("v1="))
    .some((p) => {
      const got = Buffer.from(p.slice(3), "hex");
      return got.length === expected.length && timingSafeEqual(got, expected);
    });
}

Responde 2xx em menos de 10 segundos (trata o evento depois, numa fila). Sem 2xx, há até 8 tentativas em cerca de 24 horas (1 min, 5 min, 30 min, 2 h, 5 h, 8 h e 8 h). Ao fim de 3 dias seguidos a falhar, o endpoint desliga-se e o dono é avisado na app e por email. As entregas ficam 30 dias no registo, com os primeiros 2 KB da resposta, e podem ser reenviadas (POST /v1/webhook-deliveries/{id}/redeliver). Para experimentar, POST /v1/webhook-endpoints/{id}/test envia um webhook.test. Depois de rodar o segredo, o anterior continua a assinar (segundo v1) durante 24 horas.

Eventos: absence.created, absence.updated, absence.approved, absence.rejected, absence.cancellation_requested, absence.cancelled, expense.submitted, expense.approved, expense.rejected, expense.paid, expense.cancelled, business_trip.submitted, business_trip.approved, business_trip.rejected, business_trip.cancelled, document.created, document.version_created, document.deleted, document.acknowledged, document.expiring, document.expired, document.review_requested, document.reviewed, employee.created, employee.updated, employee.status_changed, employee.contract_changed, employee.compensation_changed, employee.bank_details_change_requested, employee.bank_details_changed, employee.bank_details_change_rejected, team.created, team.updated, team.deleted, team.member_added, team.member_removed.

Apps móveis

Este guia percorre uma app móvel de férias e despesas: login nativo com OAuth, push, ecrã inicial, pedir férias e registar uma despesa com a foto do recibo. A app é uma app pública (sem segredo). Regista-a em Definições → API → As minhas apps, com o tipo pública e o redirect no esquema da app.

1. Login nativo com PKCE

TypeScript
import { OAuthSession, SelfcareClient, buildAuthorizeUrl, createPkcePair, exchangeCode, parseAuthorizationResponse, randomString } from "@selfcare/api-sdk";

const clientId = "sc_app_…";
const redirectUri = "work.selfcare.app:/oauth";
const { verifier, challenge } = await createPkcePair();
const state = randomString();
openSystemBrowser(buildAuthorizeUrl({ clientId, redirectUri, scopes: ["me:read", "absences:write"], state, codeChallenge: challenge }));

// On the redirect: check "state" and "iss" (RFC 9207), then exchange the code
const { code } = parseAuthorizationResponse(redirectedUrl, { state, issuer: "https://api.selfcare.work" });
const tokens = await exchangeCode({ clientId, code, redirectUri, codeVerifier: verifier });
const session = new OAuthSession(tokens, { clientId }, (t) => saveToKeychain(t)); // t === null: signed out
const api = new SelfcareClient({ token: () => session.accessToken(), onUnauthorized: () => session.forceRefresh() });

2. Registar o telefone e mostrar o ecrã inicial

Abre o authorization_endpoint no browser do sistema (ASWebAuthenticationSession no iOS, Custom Tabs no Android; nunca numa WebView), com um code_verifier e um state novos em cada login. Guarda os tokens no Keychain ou no Keystore. Depois do login, e em cada arranque, regista o telefone com POST /v1/me/devices: o dispositivo fica preso a esta sessão. Os push levam só type, resourceType, resourceId e uma frase curta; tipos: absence.approved, absence.rejected, approval.pending, expense.approved, expense.rejected, expense.reimbursed, document.requested, bank_details.confirmation_pending.

TypeScript
// After signing in, and on every launch
await api.registerMyDevice({ body: { platform: "ios", pushToken: apnsToken, locale: "pt-PT", appVersion: "1.0.0" } });

// Home screen: balance, upcoming absences, approvals, documents, unread notifications
const home = await api.getMySummary();
Push
// Push payload (APNs / FCM data): never names, amounts or absence types
{ "type": "absence.approved", "resourceType": "absence", "resourceId": "3f1c…" }
// On tap: api.getAbsence({ params: { id: resourceId } })

3. Pedir férias

Mostra o saldo antes de pedir. O pedido verifica o saldo, os feriados, os dias de descanso da pessoa e os períodos bloqueados como a app, e segue para os aprovadores (as equipas da pessoa) se o tipo pedir aprovação. Usa uma Idempotency-Key por pedido para que uma repetição por falha de rede não crie dois.

TypeScript
const { data: types } = await api.listAbsenceTypes();
const balance = await api.getVacationBalance({ params: { id: me.employee!.id } });

const absence = await api.createAbsence({
  body: { absenceTypeCode: "FERIAS", startDate: "2026-12-21", endDate: "2026-12-24" },
  idempotencyKey: crypto.randomUUID(), // keep it if you retry
});
// absence.status: "PENDING" (goes to the approvers) or "APPROVED"

4. Despesa com a foto do recibo

A despesa nasce em rascunho. O recibo vai direto para o armazenamento por um URL pré-assinado de 15 minutos (o bilhete vale 30); a confirmação verifica o sha256 e o tipo real do ficheiro. Depois, submete a despesa para aprovação. O estado de reembolso vê-se em status (APPROVED, PAID) e paidAt.

No logout, chama POST /oauth/revoke com o refresh token (no SDK, session.signOut()): a sessão e os dispositivos dela deixam de receber push. Se um refresh falhar com invalid_grant, volta ao login.

TypeScript
const expense = await api.createExpense({ body: { date: "2026-11-03", description: "Almoço com cliente", category: "MEALS" } });

// The receipt photo: PDF, JPEG, PNG or WebP, up to 10 MB
const upload = await api.createExpenseAttachmentUpload({
  params: { id: expense.id },
  body: { fileName: "recibo.jpg", contentType: "image/jpeg", sizeBytes: bytes.length, sha256: sha256Hex(bytes) },
});
await fetch(upload.url, { method: upload.method, headers: upload.headers, body: bytes });
await api.addExpenseAttachment({ params: { id: expense.id }, body: { ticket: upload.ticket, amountCents: 2450 } });

await api.submitExpense({ params: { id: expense.id } });

Referência

Ausências e férias

Tipos de ausência da empresa

GET/v1/absence-types
Âmbito exigidoabsences:read

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<AbsenceType>obrigatório
      Campos de AbsenceType
      • idstring (uuid)obrigatório
      • codestringobrigatório
      • namestringobrigatório
      • colorstringobrigatório
      • isPaidbooleanobrigatório
      • countsCalendarDaysbooleanobrigatório
      • countsAgainstVacationbooleanobrigatório
      • requiresApprovalbooleanobrigatório
      • maxDaysPerYearinteger | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/absence-types
Pedido
curl "https://api.selfcare.work/v1/absence-types" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "FERIAS",
      "name": "Férias",
      "color": "string",
      "isPaid": true,
      "countsCalendarDays": true,
      "countsAgainstVacation": true,
      "requiresApproval": true,
      "maxDaysPerYear": 0,
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Sobreposição com o intervalo de from a to. Os rascunhos só aparecem ao próprio.

Âmbito exigidoabsences:read

Parâmetros da query

  • from
    string (date)

    Exemplo: 2026-10-01

  • to
    string (date)

    Exemplo: 2026-10-01

  • status
    string

    Valores separados por vírgulas.

    Exemplo: APPROVED,PENDING

  • employeeId
    string (uuid)
  • teamId
    string (uuid)
  • updatedSince
    string (date-time)

    Exemplo: 2026-10-01T00:00:00Z

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<Absence>obrigatório
      Campos de Absence
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • absenceTypeAbsenceTypeRefobrigatório

        Para quem não pode ver o tipo de uma ausência de saúde, sai com id null, código AUSENCIA e nome Ausência, como na app.

        Campos de AbsenceTypeRef
        • idstring (uuid) | nullobrigatóriopode ser null
        • codestringobrigatório
        • namestringobrigatório
        • colorstringobrigatório
      • startDatestring (date)obrigatório
      • endDatestring (date)obrigatório
      • startHalfDaybooleanobrigatório
      • endHalfDaybooleanobrigatório
      • workingDaysnumberobrigatório
      • reasonstring | nullobrigatóriopode ser null

        Só para quem o vê na app: administradores, o próprio e os aprovadores.

      • statusstringobrigatório

        Valores:"DRAFT""PENDING""APPROVED""REJECTED""CANCELLED""PENDING_CANCELLATION"

      • sourcestringobrigatório
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • cancellationRequestedAtstring (date-time) | nullobrigatóriopode ser null
      • approvalsarray<object>obrigatório
        Campos
        • approverIdstring (uuid)obrigatório
        • viaTeamsstringobrigatório
        • decisionstring | nullobrigatóriopode ser null
        • decidedAtstring (date-time) | nullobrigatóriopode ser null
        • commentstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/absences
Pedido
curl "https://api.selfcare.work/v1/absences?from=2026-10-01&to=2026-10-01" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "employee": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string"
      },
      "absenceType": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "code": "string",
        "name": "string",
        "color": "string"
      },
      "startDate": "2026-10-16",
      "endDate": "2026-10-16",
      "startHalfDay": true,
      "endHalfDay": true,
      "workingDays": 0,
      "reason": "string",
      "status": "DRAFT",
      "source": "string",
      "submittedAt": "2026-10-16T09:30:00.000Z",
      "decidedAt": "2026-10-16T09:30:00.000Z",
      "cancellationRequestedAt": "2026-10-16T09:30:00.000Z",
      "approvals": [
        {
          "approverId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
          "viaTeams": "string",
          "decision": "string",
          "decidedAt": "2026-10-16T09:30:00.000Z",
          "comment": "string"
        }
      ],
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Marcar uma ausência

POST/v1/absences

Para si (sem employeeId) ou, se for RH, dono ou líder do visado, para outra pessoa. A mesma validação da app: saldo de férias, dias úteis pela escala e feriados, dias bloqueados, máximo anual do tipo. Não deixa marcar por cima de outra ausência da mesma pessoa.

Âmbito exigidoabsences:write

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • employeeIdstring (uuid)

    Por omissão, a ficha de quem usa a credencial.

  • absenceTypeCodestringobrigatório
  • startDatestring (date)obrigatório
  • endDatestring (date)obrigatório
  • startHalfDayboolean

    Meio-dia no primeiro dia.

  • endHalfDayboolean

    Meio-dia no último dia.

  • reasonstring
  • forceBlockedboolean

    Dono e RH: marcar férias por cima de um período bloqueado (fica registado).

Respostas

  • 201Criado
    • dataAbsenceobrigatório

      Nunca leva o anexo (atestado).

      Campos de Absence
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • absenceTypeAbsenceTypeRefobrigatório

        Para quem não pode ver o tipo de uma ausência de saúde, sai com id null, código AUSENCIA e nome Ausência, como na app.

        Campos de AbsenceTypeRef
        • idstring (uuid) | nullobrigatóriopode ser null
        • codestringobrigatório
        • namestringobrigatório
        • colorstringobrigatório
      • startDatestring (date)obrigatório
      • endDatestring (date)obrigatório
      • startHalfDaybooleanobrigatório
      • endHalfDaybooleanobrigatório
      • workingDaysnumberobrigatório
      • reasonstring | nullobrigatóriopode ser null

        Só para quem o vê na app: administradores, o próprio e os aprovadores.

      • statusstringobrigatório

        Valores:"DRAFT""PENDING""APPROVED""REJECTED""CANCELLED""PENDING_CANCELLATION"

      • sourcestringobrigatório
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • cancellationRequestedAtstring (date-time) | nullobrigatóriopode ser null
      • approvalsarray<object>obrigatório
        Campos
        • approverIdstring (uuid)obrigatório
        • viaTeamsstringobrigatório
        • decisionstring | nullobrigatóriopode ser null
        • decidedAtstring (date-time) | nullobrigatóriopode ser null
        • commentstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/absences
Pedido
curl -X POST "https://api.selfcare.work/v1/absences" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "absenceTypeCode": "FERIAS",
  "startDate": "2026-11-02",
  "endDate": "2026-11-06"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "absenceType": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string",
      "color": "string"
    },
    "startDate": "2026-10-16",
    "endDate": "2026-10-16",
    "startHalfDay": true,
    "endHalfDay": true,
    "workingDays": 0,
    "reason": "string",
    "status": "DRAFT",
    "source": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "cancellationRequestedAt": "2026-10-16T09:30:00.000Z",
    "approvals": [
      {
        "approverId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "viaTeams": "string",
        "decision": "string",
        "decidedAt": "2026-10-16T09:30:00.000Z",
        "comment": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Uma ausência

GET/v1/absences/{id}
Âmbito exigidoabsences:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataAbsenceobrigatório

      Nunca leva o anexo (atestado).

      Campos de Absence
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • absenceTypeAbsenceTypeRefobrigatório

        Para quem não pode ver o tipo de uma ausência de saúde, sai com id null, código AUSENCIA e nome Ausência, como na app.

        Campos de AbsenceTypeRef
        • idstring (uuid) | nullobrigatóriopode ser null
        • codestringobrigatório
        • namestringobrigatório
        • colorstringobrigatório
      • startDatestring (date)obrigatório
      • endDatestring (date)obrigatório
      • startHalfDaybooleanobrigatório
      • endHalfDaybooleanobrigatório
      • workingDaysnumberobrigatório
      • reasonstring | nullobrigatóriopode ser null

        Só para quem o vê na app: administradores, o próprio e os aprovadores.

      • statusstringobrigatório

        Valores:"DRAFT""PENDING""APPROVED""REJECTED""CANCELLED""PENDING_CANCELLATION"

      • sourcestringobrigatório
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • cancellationRequestedAtstring (date-time) | nullobrigatóriopode ser null
      • approvalsarray<object>obrigatório
        Campos
        • approverIdstring (uuid)obrigatório
        • viaTeamsstringobrigatório
        • decisionstring | nullobrigatóriopode ser null
        • decidedAtstring (date-time) | nullobrigatóriopode ser null
        • commentstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/absences/{id}
Pedido
curl "https://api.selfcare.work/v1/absences/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "absenceType": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string",
      "color": "string"
    },
    "startDate": "2026-10-16",
    "endDate": "2026-10-16",
    "startHalfDay": true,
    "endHalfDay": true,
    "workingDays": 0,
    "reason": "string",
    "status": "DRAFT",
    "source": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "cancellationRequestedAt": "2026-10-16T09:30:00.000Z",
    "approvals": [
      {
        "approverId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "viaTeams": "string",
        "decision": "string",
        "decidedAt": "2026-10-16T09:30:00.000Z",
        "comment": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Corrigir um pedido pendente

PATCH/v1/absences/{id}

Só enquanto ninguém decidiu nada. Valida tudo de novo e recomeça a cadeia de aprovação. Aceita If-Match.

Âmbito exigidoabsences:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • absenceTypeCodestring
  • startDatestring (date)
  • endDatestring (date)
  • startHalfDayboolean

    Meio-dia no primeiro dia.

  • endHalfDayboolean

    Meio-dia no último dia.

  • reasonstring
  • forceBlockedboolean

    Dono e RH: marcar férias por cima de um período bloqueado (fica registado).

Respostas

  • 200OK
    • dataAbsenceobrigatório

      Nunca leva o anexo (atestado).

      Campos de Absence
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • absenceTypeAbsenceTypeRefobrigatório

        Para quem não pode ver o tipo de uma ausência de saúde, sai com id null, código AUSENCIA e nome Ausência, como na app.

        Campos de AbsenceTypeRef
        • idstring (uuid) | nullobrigatóriopode ser null
        • codestringobrigatório
        • namestringobrigatório
        • colorstringobrigatório
      • startDatestring (date)obrigatório
      • endDatestring (date)obrigatório
      • startHalfDaybooleanobrigatório
      • endHalfDaybooleanobrigatório
      • workingDaysnumberobrigatório
      • reasonstring | nullobrigatóriopode ser null

        Só para quem o vê na app: administradores, o próprio e os aprovadores.

      • statusstringobrigatório

        Valores:"DRAFT""PENDING""APPROVED""REJECTED""CANCELLED""PENDING_CANCELLATION"

      • sourcestringobrigatório
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • cancellationRequestedAtstring (date-time) | nullobrigatóriopode ser null
      • approvalsarray<object>obrigatório
        Campos
        • approverIdstring (uuid)obrigatório
        • viaTeamsstringobrigatório
        • decisionstring | nullobrigatóriopode ser null
        • decidedAtstring (date-time) | nullobrigatóriopode ser null
        • commentstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/absences/{id}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/absences/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "absenceTypeCode": "FERIAS",
  "startDate": "2026-11-02",
  "endDate": "2026-11-06"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "absenceType": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string",
      "color": "string"
    },
    "startDate": "2026-10-16",
    "endDate": "2026-10-16",
    "startHalfDay": true,
    "endHalfDay": true,
    "workingDays": 0,
    "reason": "string",
    "status": "DRAFT",
    "source": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "cancellationRequestedAt": "2026-10-16T09:30:00.000Z",
    "approvals": [
      {
        "approverId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "viaTeams": "string",
        "decision": "string",
        "decidedAt": "2026-10-16T09:30:00.000Z",
        "comment": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Aprovar uma ausência

POST/v1/absences/{id}/approve

A regra da app: o aprovador decide a sua linha (ALL ou ANY); RH e dono por cima, nunca as próprias (exceto o dono).

Âmbito exigidoabsences:decide

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • commentstring

Respostas

  • 200OK
    • dataAbsenceobrigatório

      Nunca leva o anexo (atestado).

      Campos de Absence
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • absenceTypeAbsenceTypeRefobrigatório

        Para quem não pode ver o tipo de uma ausência de saúde, sai com id null, código AUSENCIA e nome Ausência, como na app.

        Campos de AbsenceTypeRef
        • idstring (uuid) | nullobrigatóriopode ser null
        • codestringobrigatório
        • namestringobrigatório
        • colorstringobrigatório
      • startDatestring (date)obrigatório
      • endDatestring (date)obrigatório
      • startHalfDaybooleanobrigatório
      • endHalfDaybooleanobrigatório
      • workingDaysnumberobrigatório
      • reasonstring | nullobrigatóriopode ser null

        Só para quem o vê na app: administradores, o próprio e os aprovadores.

      • statusstringobrigatório

        Valores:"DRAFT""PENDING""APPROVED""REJECTED""CANCELLED""PENDING_CANCELLATION"

      • sourcestringobrigatório
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • cancellationRequestedAtstring (date-time) | nullobrigatóriopode ser null
      • approvalsarray<object>obrigatório
        Campos
        • approverIdstring (uuid)obrigatório
        • viaTeamsstringobrigatório
        • decisionstring | nullobrigatóriopode ser null
        • decidedAtstring (date-time) | nullobrigatóriopode ser null
        • commentstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/absences/{id}/approve
Pedido
curl -X POST "https://api.selfcare.work/v1/absences/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/approve" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "comment": "string"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "absenceType": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string",
      "color": "string"
    },
    "startDate": "2026-10-16",
    "endDate": "2026-10-16",
    "startHalfDay": true,
    "endHalfDay": true,
    "workingDays": 0,
    "reason": "string",
    "status": "DRAFT",
    "source": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "cancellationRequestedAt": "2026-10-16T09:30:00.000Z",
    "approvals": [
      {
        "approverId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "viaTeams": "string",
        "decision": "string",
        "decidedAt": "2026-10-16T09:30:00.000Z",
        "comment": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Rejeitar uma ausência

POST/v1/absences/{id}/reject

A mesma regra de aprovar.

Âmbito exigidoabsences:decide

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • commentstring

Respostas

  • 200OK
    • dataAbsenceobrigatório

      Nunca leva o anexo (atestado).

      Campos de Absence
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • absenceTypeAbsenceTypeRefobrigatório

        Para quem não pode ver o tipo de uma ausência de saúde, sai com id null, código AUSENCIA e nome Ausência, como na app.

        Campos de AbsenceTypeRef
        • idstring (uuid) | nullobrigatóriopode ser null
        • codestringobrigatório
        • namestringobrigatório
        • colorstringobrigatório
      • startDatestring (date)obrigatório
      • endDatestring (date)obrigatório
      • startHalfDaybooleanobrigatório
      • endHalfDaybooleanobrigatório
      • workingDaysnumberobrigatório
      • reasonstring | nullobrigatóriopode ser null

        Só para quem o vê na app: administradores, o próprio e os aprovadores.

      • statusstringobrigatório

        Valores:"DRAFT""PENDING""APPROVED""REJECTED""CANCELLED""PENDING_CANCELLATION"

      • sourcestringobrigatório
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • cancellationRequestedAtstring (date-time) | nullobrigatóriopode ser null
      • approvalsarray<object>obrigatório
        Campos
        • approverIdstring (uuid)obrigatório
        • viaTeamsstringobrigatório
        • decisionstring | nullobrigatóriopode ser null
        • decidedAtstring (date-time) | nullobrigatóriopode ser null
        • commentstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/absences/{id}/reject
Pedido
curl -X POST "https://api.selfcare.work/v1/absences/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/reject" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "comment": "string"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "absenceType": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string",
      "color": "string"
    },
    "startDate": "2026-10-16",
    "endDate": "2026-10-16",
    "startHalfDay": true,
    "endHalfDay": true,
    "workingDays": 0,
    "reason": "string",
    "status": "DRAFT",
    "source": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "cancellationRequestedAt": "2026-10-16T09:30:00.000Z",
    "approvals": [
      {
        "approverId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "viaTeams": "string",
        "decision": "string",
        "decidedAt": "2026-10-16T09:30:00.000Z",
        "comment": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Cancelar uma ausência

POST/v1/absences/{id}/cancel

Um pedido pendente cancela-se logo. Uma ausência aprovada cancela-se logo ou fica com o cancelamento à espera de aprovação (PENDING_CANCELLATION), como na app.

Âmbito exigidoabsences:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataAbsenceobrigatório

      Nunca leva o anexo (atestado).

      Campos de Absence
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • absenceTypeAbsenceTypeRefobrigatório

        Para quem não pode ver o tipo de uma ausência de saúde, sai com id null, código AUSENCIA e nome Ausência, como na app.

        Campos de AbsenceTypeRef
        • idstring (uuid) | nullobrigatóriopode ser null
        • codestringobrigatório
        • namestringobrigatório
        • colorstringobrigatório
      • startDatestring (date)obrigatório
      • endDatestring (date)obrigatório
      • startHalfDaybooleanobrigatório
      • endHalfDaybooleanobrigatório
      • workingDaysnumberobrigatório
      • reasonstring | nullobrigatóriopode ser null

        Só para quem o vê na app: administradores, o próprio e os aprovadores.

      • statusstringobrigatório

        Valores:"DRAFT""PENDING""APPROVED""REJECTED""CANCELLED""PENDING_CANCELLATION"

      • sourcestringobrigatório
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • cancellationRequestedAtstring (date-time) | nullobrigatóriopode ser null
      • approvalsarray<object>obrigatório
        Campos
        • approverIdstring (uuid)obrigatório
        • viaTeamsstringobrigatório
        • decisionstring | nullobrigatóriopode ser null
        • decidedAtstring (date-time) | nullobrigatóriopode ser null
        • commentstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/absences/{id}/cancel
Pedido
curl -X POST "https://api.selfcare.work/v1/absences/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/cancel" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "absenceType": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string",
      "color": "string"
    },
    "startDate": "2026-10-16",
    "endDate": "2026-10-16",
    "startHalfDay": true,
    "endHalfDay": true,
    "workingDays": 0,
    "reason": "string",
    "status": "DRAFT",
    "source": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "cancellationRequestedAt": "2026-10-16T09:30:00.000Z",
    "approvals": [
      {
        "approverId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "viaTeams": "string",
        "decision": "string",
        "decidedAt": "2026-10-16T09:30:00.000Z",
        "comment": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Saldo de férias de um colaborador

GET/v1/employees/{id}/vacation-balance

Para quem vê as ausências da pessoa na app.

Âmbito exigidoabsences:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Parâmetros da query

  • year
    integer

Respostas

  • 200OK
    • dataVacationBalanceobrigatório
      Campos de VacationBalance
      • employeeIdstring (uuid)obrigatório
      • yearintegerobrigatório
      • entitlednumberobrigatório

        Direito do ano (pro-rata no ano de admissão).

      • carriedOvernumberobrigatório

        Transitados do ano anterior.

      • usednumberobrigatório

        Gozados (aprovados e já passados).

      • schedulednumberobrigatório

        Marcados (aprovados futuros e pendentes).

      • availablenumberobrigatório

        Por gozar: direito + transitados − gozados − marcados.

      • carryRemainingnumberobrigatório
      • carryWindowOpenbooleanobrigatório
      • carryDeadlinestringobrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/employees/{id}/vacation-balance
Pedido
curl "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/vacation-balance" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "year": 0,
    "entitled": 0,
    "carriedOver": 0,
    "used": 0,
    "scheduled": 0,
    "available": 0,
    "carryRemaining": 0,
    "carryWindowOpen": true,
    "carryDeadline": "30/04"
  },
  "error": null
}

Referência

Aprovações

O que o utilizador tem para decidir, como na app. Despesas, deslocações e documentos só aparecem se a credencial tiver expenses:read, trips:read e documents:read.

Âmbito exigidoabsences:read

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<ApprovalItem>obrigatório
      Campos de ApprovalItem
      • idstringobrigatório

        <kind>:<id do recurso>, estável.

      • kindstringobrigatório

        Valores:"absence""absence_cancellation""expense""business_trip""document_review"

      • resourceIdstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • summarystringobrigatório

        Datas, valor ou título; sem dados de saúde.

      • viaTeamsarray<string>obrigatório
      • createdAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/approvals
Pedido
curl "https://api.selfcare.work/v1/approvals" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "string",
      "kind": "absence",
      "resourceId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "employee": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string"
      },
      "summary": "string",
      "viaTeams": [
        "string"
      ],
      "createdAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Referência

Eu

Os meus dispositivos

GET/v1/me/devices

Os telefones de quem pede registados para notificações push, nesta empresa. O token de push nunca volta.

Âmbito exigidome:read

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<MobileDevice>obrigatório
      Campos de MobileDevice
      • idstring (uuid)obrigatório
      • platformstringobrigatório

        Valores:"ios""android"

      • localestringobrigatório

        Idioma das notificações deste telefone (pt ou en).

        Valores:"pt""en"

      • appVersionstring | nullobrigatóriopode ser null
      • appobject | nullobrigatóriopode ser null

        A app OAuth que registou o dispositivo.

        Campos
        • clientIdstringobrigatório
        • namestringobrigatório
      • lastSeenAtstring (date-time)obrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/me/devices
Pedido
curl "https://api.selfcare.work/v1/me/devices" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "platform": "ios",
      "locale": "pt",
      "appVersion": "string",
      "app": {
        "clientId": "string",
        "name": "string"
      },
      "lastSeenAt": "2026-10-16T09:30:00.000Z",
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Só com um token OAuth: o dispositivo fica preso a essa sessão, e terminar a sessão (revogar o refresh token) ou revogar a app desliga-o. Chame-o em cada arranque da app: o mesmo token de push atualiza o registo. O token guarda-se cifrado; sem cifra configurada, 503. Um token de push que passe para outra pessoa deixa de receber os avisos da anterior.

Âmbito exigidome:read

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • platformstringobrigatório

    Valores:"ios""android"

  • pushTokenstringobrigatório

    O token do APNs (iOS) ou do FCM (Android). Guarda-se cifrado e nunca volta numa resposta.

  • localestring | nullpode ser null
  • appVersionstring | nullpode ser null

Respostas

  • 201Criado
    • dataMobileDeviceobrigatório
      Campos de MobileDevice
      • idstring (uuid)obrigatório
      • platformstringobrigatório

        Valores:"ios""android"

      • localestringobrigatório

        Idioma das notificações deste telefone (pt ou en).

        Valores:"pt""en"

      • appVersionstring | nullobrigatóriopode ser null
      • appobject | nullobrigatóriopode ser null

        A app OAuth que registou o dispositivo.

        Campos
        • clientIdstringobrigatório
        • namestringobrigatório
      • lastSeenAtstring (date-time)obrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado
  • 503Serviço indisponível (por exemplo, sem a chave de cifra configurada)

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/me/devices
Pedido
curl -X POST "https://api.selfcare.work/v1/me/devices" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "platform": "ios",
  "pushToken": "string",
  "locale": "pt-PT",
  "appVersion": "1.0.0 (42)"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "platform": "ios",
    "locale": "pt",
    "appVersion": "string",
    "app": {
      "clientId": "string",
      "name": "string"
    },
    "lastSeenAt": "2026-10-16T09:30:00.000Z",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Apagar um dispositivo

DELETE/v1/me/devices/{id}

Deixa de receber push. O dispositivo de outra pessoa é 404.

Âmbito exigidome:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Respostas

  • 200OK
    • dataMobileDeviceDeletedobrigatório
      Campos de MobileDeviceDeleted
      • idstring (uuid)obrigatório
      • deletedbooleanobrigatório

        Valores:true

    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

DELETEhttps://api.selfcare.work/v1/me/devices/{id}
Pedido
curl -X DELETE "https://api.selfcare.work/v1/me/devices/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "deleted": true
  },
  "error": null
}

Saldo de férias, próximas ausências, pedidos à espera da minha decisão, documentos para ler, IBAN por confirmar e avisos por ler, numa só chamada. Cada bloco segue o âmbito do endpoint que o lê por inteiro e vem null sem ele.

Âmbito exigidome:read

Respostas

  • 200OK
    • dataMeSummaryobrigatório
      Campos de MeSummary
      • vacationBalanceVacationBalance | nullobrigatóriopode ser null

        Saldo de férias do ano corrente (âmbito absences:read).

        Campos de VacationBalance
        • employeeIdstring (uuid)obrigatório
        • yearintegerobrigatório
        • entitlednumberobrigatório

          Direito do ano (pro-rata no ano de admissão).

        • carriedOvernumberobrigatório

          Transitados do ano anterior.

        • usednumberobrigatório

          Gozados (aprovados e já passados).

        • schedulednumberobrigatório

          Marcados (aprovados futuros e pendentes).

        • availablenumberobrigatório

          Por gozar: direito + transitados − gozados − marcados.

        • carryRemainingnumberobrigatório
        • carryWindowOpenbooleanobrigatório
        • carryDeadlinestringobrigatório
      • upcomingAbsencesarray<object> | nullobrigatóriopode ser null

        As próximas 5 ausências, pedidas ou aprovadas (âmbito absences:read).

        Campos
        • idstring (uuid)obrigatório
        • startDatestringobrigatório
        • endDatestringobrigatório
        • statusstringobrigatório
        • workingDaysnumberobrigatório
      • approvalsPendinginteger | nullobrigatóriopode ser null

        Pedidos na caixa de Aprovações à espera da minha decisão (âmbito absences:read).

      • documentsToAcknowledgeinteger | nullobrigatóriopode ser null

        Documentos à espera de que confirme a leitura (âmbito documents:read).

      • bankDetailsConfirmationPendingbooleanobrigatório
      • unreadNotificationsintegerobrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/me/summary
Pedido
curl "https://api.selfcare.work/v1/me/summary" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "vacationBalance": {
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "year": 0,
      "entitled": 0,
      "carriedOver": 0,
      "used": 0,
      "scheduled": 0,
      "available": 0,
      "carryRemaining": 0,
      "carryWindowOpen": true,
      "carryDeadline": "30/04"
    },
    "upcomingAbsences": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "startDate": "string",
        "endDate": "string",
        "status": "string",
        "workingDays": 0
      }
    ],
    "approvalsPending": 0,
    "documentsToAcknowledge": 0,
    "bankDetailsConfirmationPending": true,
    "unreadNotifications": 0
  },
  "error": null
}

Utilizador, empresa (a da credencial), papel, ficha, equipas que lidera e âmbitos.

Âmbito exigidome:read

Respostas

  • 200OK
    • dataMeobrigatório
      Campos de Me
      • userobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • emailstringobrigatório
        • namestring | nullobrigatóriopode ser null
      • tenantobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • namestringobrigatório
        • slugstring | nullobrigatóriopode ser null
        • timeZonestringobrigatório
        • sandboxbooleanobrigatório
        • displayNamestringobrigatório

          O nome que a empresa mostra às pessoas (o do portal, ou o nome legal se não houver). É o que um seletor de contas deve mostrar.

        • logoUrlstring | nullobrigatóriopode ser null

          O logótipo da empresa (URL público), ou null se não tiver.

        • brandColorstringobrigatório

          A cor principal da paleta da empresa (Definições → Aparência), em #RRGGBB.

      • rolestringobrigatório
      • employeeobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • preferredNamestring | nullobrigatóriopode ser null
        • employeeNumberstringobrigatório
        • jobTitlestring | nullobrigatóriopode ser null
        • photoUrlstring | nullobrigatóriopode ser null
        • statusstringobrigatório

          Valores:"ACTIVE""ON_LEAVE""TERMINATED""ARCHIVED"

        • kindstringobrigatório
        • workEmailstring | nullobrigatóriopode ser null
        • teamsarray<TeamRef>obrigatório
          Campos de TeamRef
          • idstring (uuid)obrigatório
          • namestringobrigatório
      • ledTeamsarray<TeamRef>obrigatório
        Campos de TeamRef
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • credentialobjectobrigatório
        Campos
        • typestringobrigatório

          Valores:"api_key""oauth"

        • scopesarray<string>obrigatório

          Valores:"me:read""absences:read""absences:write""absences:decide""expenses:read""expenses:write""expenses:decide""trips:read""trips:write""trips:decide""documents:read""documents:write""employees:read""employees:write""teams:read""teams:write""webhooks:manage""contracts:read""contracts:write""compensation:read""compensation:write""bank-details:write"

        • environmentstringobrigatório

          Valores:"LIVE""TEST"

    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/me
Pedido
curl "https://api.selfcare.work/v1/me" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "user": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "email": "string",
      "name": "string"
    },
    "tenant": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "name": "string",
      "slug": "string",
      "timeZone": "string",
      "sandbox": true,
      "displayName": "string",
      "logoUrl": "string",
      "brandColor": "#EA580C"
    },
    "role": "HR_ADMIN",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "preferredName": "string",
      "employeeNumber": "string",
      "jobTitle": "string",
      "photoUrl": "string",
      "status": "ACTIVE",
      "kind": "string",
      "workEmail": "string",
      "teams": [
        {
          "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
          "name": "string"
        }
      ]
    },
    "ledTeams": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "name": "string"
      }
    ],
    "credential": {
      "type": "api_key",
      "scopes": [
        "me:read"
      ],
      "environment": "LIVE"
    }
  },
  "error": null
}

Referência

Despesas

As do próprio (incluindo rascunhos); as da equipa para quem lidera; as da empresa para dono, RH e contabilista. Lista vazia com o módulo desligado.

Âmbito exigidoexpenses:read

Parâmetros da query

  • status
    string

    Valores separados por vírgulas.

    Exemplo: APPROVED,PAID

  • employeeId
    string (uuid)
  • from
    string (date)

    Exemplo: 2026-10-01

  • to
    string (date)

    Exemplo: 2026-10-01

  • updatedSince
    string (date-time)

    Exemplo: 2026-10-01T00:00:00Z

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<Expense>obrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/expenses
Pedido
curl "https://api.selfcare.work/v1/expenses?status=APPROVED,PAID&from=2026-10-01" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "employee": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string"
      },
      "date": "2026-10-16",
      "description": "string",
      "category": "MEALS",
      "amountCents": 0,
      "currency": "EUR",
      "status": "DRAFT",
      "rejectionNote": "string",
      "submittedAt": "2026-10-16T09:30:00.000Z",
      "decidedAt": "2026-10-16T09:30:00.000Z",
      "decidedByName": "string",
      "paidAt": "2026-10-16T09:30:00.000Z",
      "attachments": [
        {
          "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
          "fileName": "string",
          "contentType": "string",
          "sizeBytes": 0,
          "amountCents": 0
        }
      ],
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Do próprio, ou em nome de alguém por quem gere as despesas. Depois juntam-se os recibos e submete-se.

Âmbito exigidoexpenses:write

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • employeeIdstring (uuid)

    Em nome de outra pessoa: só quem gere as despesas.

  • datestring (date)
  • descriptionstring
  • categorystring

    Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

Respostas

  • 201Criado
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/expenses
Pedido
curl -X POST "https://api.selfcare.work/v1/expenses" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "date": "2026-10-15"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Uma despesa

GET/v1/expenses/{id}
Âmbito exigidoexpenses:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/expenses/{id}
Pedido
curl "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Editar uma despesa

PATCH/v1/expenses/{id}

Rascunhos e rejeitadas. Aceita If-Match.

Âmbito exigidoexpenses:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • datestring (date)
  • descriptionstring
  • categorystring

    Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

Respostas

  • 200OK
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/expenses/{id}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "date": "2026-10-15"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Descarregar um anexo de despesa

GET/v1/expenses/{id}/attachments/{attachmentId}/content

Devolve um URL pré-assinado válido durante 5 minutos, nunca um redireccionamento.

Âmbito exigidoexpenses:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)
  • attachmentId
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataFileLinkobrigatório
      Campos de FileLink
      • urlstring (uri)obrigatório

        URL pré-assinado de GET, válido 5 minutos. Usar já; não guardar nem partilhar.

      • expiresAtstring (date-time)obrigatório
      • fileNamestringobrigatório
      • contentTypestringobrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/expenses/{id}/attachments/{attachmentId}/content
Pedido
curl "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/attachments/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/content" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "url": "https://example.com",
    "expiresAt": "2026-10-16T09:30:00.000Z",
    "fileName": "string",
    "contentType": "string"
  },
  "error": null
}

Categorias de despesa

GET/v1/expense-categories

O nome vem na língua do Accept-Language.

Âmbito exigidoexpenses:read

Respostas

  • 200OK
    • dataarray<ExpenseCategory>obrigatório
      Campos de ExpenseCategory
      • codestringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • namestringobrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/expense-categories
Pedido
curl "https://api.selfcare.work/v1/expense-categories" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "code": "MEALS",
      "name": "string"
    }
  ],
  "error": null
}

Pedir o URL para um recibo

POST/v1/expenses/{id}/attachments/upload-url

Devolve um URL de PUT (15 minutos) e um bilhete (30 minutos). PDF, JPEG, PNG ou WebP, até 10 MB, como na app.

Âmbito exigidoexpenses:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • fileNamestringobrigatório
  • contentTypestringobrigatório
  • sizeBytesintegerobrigatório
  • sha256stringobrigatório

    sha256 dos bytes, em hexadecimal; confere-se ao confirmar.

Respostas

  • 200OK
    • dataUploadUrlobrigatório
      Campos de UploadUrl
      • urlstringobrigatório

        URL pré-assinado para o PUT dos bytes (15 minutos).

      • methodstringobrigatório

        Valores:"PUT"

      • headersobject<string, string>obrigatório

        Cabeçalhos que o PUT tem de levar.

      • ticketstringobrigatório

        Bilhete para confirmar o upload (30 minutos).

      • expiresAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/expenses/{id}/attachments/upload-url
Pedido
curl -X POST "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/attachments/upload-url" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "fileName": "string",
  "contentType": "image/jpeg",
  "sizeBytes": 0,
  "sha256": "string"
}'
Resposta

OK

{
  "data": {
    "url": "string",
    "method": "PUT",
    "headers": {},
    "ticket": "string",
    "expiresAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Juntar um recibo

POST/v1/expenses/{id}/attachments

Depois do PUT. Confere o sha256 declarado e o tipo real pelos bytes.

Âmbito exigidoexpenses:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • ticketstringobrigatório
  • amountCentsintegerobrigatório

Respostas

  • 201Criado
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/expenses/{id}/attachments
Pedido
curl -X POST "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/attachments" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "ticket": "string",
  "amountCents": 0
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Corrigir o valor de um recibo

PATCH/v1/expenses/{id}/attachments/{attachmentId}
Âmbito exigidoexpenses:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)
  • attachmentId
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • amountCentsintegerobrigatório

Respostas

  • 200OK
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/expenses/{id}/attachments/{attachmentId}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/attachments/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "amountCents": 0
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Tirar um recibo

DELETE/v1/expenses/{id}/attachments/{attachmentId}
Âmbito exigidoexpenses:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)
  • attachmentId
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Respostas

  • 200OK
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

DELETEhttps://api.selfcare.work/v1/expenses/{id}/attachments/{attachmentId}
Pedido
curl -X DELETE "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/attachments/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Submeter para reembolso

POST/v1/expenses/{id}/submit

As mesmas verificações da app e o mesmo aviso a quem decide.

Âmbito exigidoexpenses:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/expenses/{id}/submit
Pedido
curl -X POST "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/submit" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Cancelar uma despesa

POST/v1/expenses/{id}/cancel

Enquanto não está paga nem num lote de pagamento.

Âmbito exigidoexpenses:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/expenses/{id}/cancel
Pedido
curl -X POST "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/cancel" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Aprovar uma despesa

POST/v1/expenses/{id}/approve

RH, dono, contabilista e líderes dos membros; as próprias, só o dono.

Âmbito exigidoexpenses:decide

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/expenses/{id}/approve
Pedido
curl -X POST "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/approve" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Rejeitar uma despesa

POST/v1/expenses/{id}/reject

Com a nota obrigatória, que o colaborador vê.

Âmbito exigidoexpenses:decide

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • notestringobrigatório

Respostas

  • 200OK
    • dataExpenseobrigatório
      Campos de Expense
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • datestring (date)obrigatório
      • descriptionstringobrigatório
      • categorystringobrigatório

        Valores:"MEALS""TRANSPORT""FUEL_TOLLS""ACCOMMODATION""TECHNOLOGY""OFFICE""TRAINING""REPRESENTATION""OTHER"

      • amountCentsintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • statusstringobrigatório

        Valores:"DRAFT""SUBMITTED""APPROVED""REJECTED""PAID""CANCELLED"

      • rejectionNotestring | nullobrigatóriopode ser null
      • submittedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • attachmentsarray<ExpenseAttachment>obrigatório
        Campos de ExpenseAttachment
        • idstring (uuid)obrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • amountCentsintegerobrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/expenses/{id}/reject
Pedido
curl -X POST "https://api.selfcare.work/v1/expenses/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/reject" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "note": "string"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "date": "2026-10-16",
    "description": "string",
    "category": "MEALS",
    "amountCents": 0,
    "currency": "EUR",
    "status": "DRAFT",
    "rejectionNote": "string",
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "attachments": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "amountCents": 0
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Referência

Ajudas de custo

Lista vazia com o módulo desligado.

Âmbito exigidotrips:read

Parâmetros da query

  • status
    string

    Valores separados por vírgulas.

    Exemplo: APPROVED

  • employeeId
    string (uuid)
  • from
    string (date)

    Exemplo: 2026-10-01

  • to
    string (date)

    Exemplo: 2026-10-01

  • updatedSince
    string (date-time)

    Exemplo: 2026-10-01T00:00:00Z

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<BusinessTrip>obrigatório
      Campos de BusinessTrip
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • destinationstringobrigatório
      • purposestringobrigatório
      • departureAtstringobrigatório

        Hora local de Portugal, sem fuso.

      • returnAtstringobrigatório
      • abroadbooleanobrigatório
      • statusstringobrigatório

        Valores:"SUBMITTED""APPROVED""REJECTED""CANCELLED"

      • modestringobrigatório
      • nightsintegerobrigatório
      • legalPercentintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • amountCentsintegerobrigatório
      • exemptAmountCentsintegerobrigatório
      • taxableAmountCentsintegerobrigatório
      • submittedAtstring (date-time)obrigatório
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • rejectionNotestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/business-trips
Pedido
curl "https://api.selfcare.work/v1/business-trips?status=APPROVED&from=2026-10-01" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "employee": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string"
      },
      "destination": "string",
      "purpose": "string",
      "departureAt": "2026-10-05T09:00",
      "returnAt": "2026-10-06T18:30",
      "abroad": true,
      "status": "SUBMITTED",
      "mode": "string",
      "nights": 0,
      "legalPercent": 0,
      "currency": "EUR",
      "amountCents": 0,
      "exemptAmountCents": 0,
      "taxableAmountCents": 0,
      "submittedAt": "2026-10-16T09:30:00.000Z",
      "decidedAt": "2026-10-16T09:30:00.000Z",
      "decidedByName": "string",
      "rejectionNote": "string",
      "paidAt": "2026-10-16T09:30:00.000Z",
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Registar uma deslocação

POST/v1/business-trips

Do próprio. Registar é submeter; o valor sai da política da empresa ou à mão.

Âmbito exigidotrips:write

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • destinationstringobrigatório
  • purposestringobrigatório
  • departureAtstringobrigatório

    Hora local de Portugal, sem fuso.

  • returnAtstringobrigatório
  • abroadbooleanobrigatório
  • distanceConfirmedbooleanobrigatório

    Confirma a distância mínima da lei (20 km sem pernoita, 50 km com).

    Valores:true

  • manualAmountCentsinteger

    Valor à mão, em cêntimos; sem ele, o cálculo da política.

Respostas

  • 201Criado
    • dataBusinessTripobrigatório
      Campos de BusinessTrip
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • destinationstringobrigatório
      • purposestringobrigatório
      • departureAtstringobrigatório

        Hora local de Portugal, sem fuso.

      • returnAtstringobrigatório
      • abroadbooleanobrigatório
      • statusstringobrigatório

        Valores:"SUBMITTED""APPROVED""REJECTED""CANCELLED"

      • modestringobrigatório
      • nightsintegerobrigatório
      • legalPercentintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • amountCentsintegerobrigatório
      • exemptAmountCentsintegerobrigatório
      • taxableAmountCentsintegerobrigatório
      • submittedAtstring (date-time)obrigatório
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • rejectionNotestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/business-trips
Pedido
curl -X POST "https://api.selfcare.work/v1/business-trips" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "destination": "string",
  "purpose": "string",
  "departureAt": "2026-10-05T09:00",
  "returnAt": "2026-10-06T18:30",
  "abroad": true,
  "distanceConfirmed": true
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "destination": "string",
    "purpose": "string",
    "departureAt": "2026-10-05T09:00",
    "returnAt": "2026-10-06T18:30",
    "abroad": true,
    "status": "SUBMITTED",
    "mode": "string",
    "nights": 0,
    "legalPercent": 0,
    "currency": "EUR",
    "amountCents": 0,
    "exemptAmountCents": 0,
    "taxableAmountCents": 0,
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "rejectionNote": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Uma deslocação

GET/v1/business-trips/{id}
Âmbito exigidotrips:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataBusinessTripobrigatório
      Campos de BusinessTrip
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • destinationstringobrigatório
      • purposestringobrigatório
      • departureAtstringobrigatório

        Hora local de Portugal, sem fuso.

      • returnAtstringobrigatório
      • abroadbooleanobrigatório
      • statusstringobrigatório

        Valores:"SUBMITTED""APPROVED""REJECTED""CANCELLED"

      • modestringobrigatório
      • nightsintegerobrigatório
      • legalPercentintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • amountCentsintegerobrigatório
      • exemptAmountCentsintegerobrigatório
      • taxableAmountCentsintegerobrigatório
      • submittedAtstring (date-time)obrigatório
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • rejectionNotestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/business-trips/{id}
Pedido
curl "https://api.selfcare.work/v1/business-trips/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "destination": "string",
    "purpose": "string",
    "departureAt": "2026-10-05T09:00",
    "returnAt": "2026-10-06T18:30",
    "abroad": true,
    "status": "SUBMITTED",
    "mode": "string",
    "nights": 0,
    "legalPercent": 0,
    "currency": "EUR",
    "amountCents": 0,
    "exemptAmountCents": 0,
    "taxableAmountCents": 0,
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "rejectionNote": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Corrigir uma deslocação

PATCH/v1/business-trips/{id}

Ainda por decidir, ou rejeitada (volta a ser submetida). Aceita If-Match.

Âmbito exigidotrips:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • destinationstringobrigatório
  • purposestringobrigatório
  • departureAtstringobrigatório

    Hora local de Portugal, sem fuso.

  • returnAtstringobrigatório
  • abroadbooleanobrigatório
  • distanceConfirmedbooleanobrigatório

    Confirma a distância mínima da lei (20 km sem pernoita, 50 km com).

    Valores:true

  • manualAmountCentsinteger

    Valor à mão, em cêntimos; sem ele, o cálculo da política.

Respostas

  • 200OK
    • dataBusinessTripobrigatório
      Campos de BusinessTrip
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • destinationstringobrigatório
      • purposestringobrigatório
      • departureAtstringobrigatório

        Hora local de Portugal, sem fuso.

      • returnAtstringobrigatório
      • abroadbooleanobrigatório
      • statusstringobrigatório

        Valores:"SUBMITTED""APPROVED""REJECTED""CANCELLED"

      • modestringobrigatório
      • nightsintegerobrigatório
      • legalPercentintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • amountCentsintegerobrigatório
      • exemptAmountCentsintegerobrigatório
      • taxableAmountCentsintegerobrigatório
      • submittedAtstring (date-time)obrigatório
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • rejectionNotestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/business-trips/{id}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/business-trips/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "destination": "string",
  "purpose": "string",
  "departureAt": "2026-10-05T09:00",
  "returnAt": "2026-10-06T18:30",
  "abroad": true,
  "distanceConfirmed": true
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "destination": "string",
    "purpose": "string",
    "departureAt": "2026-10-05T09:00",
    "returnAt": "2026-10-06T18:30",
    "abroad": true,
    "status": "SUBMITTED",
    "mode": "string",
    "nights": 0,
    "legalPercent": 0,
    "currency": "EUR",
    "amountCents": 0,
    "exemptAmountCents": 0,
    "taxableAmountCents": 0,
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "rejectionNote": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Cancelar uma deslocação

POST/v1/business-trips/{id}/cancel
Âmbito exigidotrips:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataBusinessTripobrigatório
      Campos de BusinessTrip
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • destinationstringobrigatório
      • purposestringobrigatório
      • departureAtstringobrigatório

        Hora local de Portugal, sem fuso.

      • returnAtstringobrigatório
      • abroadbooleanobrigatório
      • statusstringobrigatório

        Valores:"SUBMITTED""APPROVED""REJECTED""CANCELLED"

      • modestringobrigatório
      • nightsintegerobrigatório
      • legalPercentintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • amountCentsintegerobrigatório
      • exemptAmountCentsintegerobrigatório
      • taxableAmountCentsintegerobrigatório
      • submittedAtstring (date-time)obrigatório
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • rejectionNotestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/business-trips/{id}/cancel
Pedido
curl -X POST "https://api.selfcare.work/v1/business-trips/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/cancel" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "destination": "string",
    "purpose": "string",
    "departureAt": "2026-10-05T09:00",
    "returnAt": "2026-10-06T18:30",
    "abroad": true,
    "status": "SUBMITTED",
    "mode": "string",
    "nights": 0,
    "legalPercent": 0,
    "currency": "EUR",
    "amountCents": 0,
    "exemptAmountCents": 0,
    "taxableAmountCents": 0,
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "rejectionNote": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Aprovar uma deslocação

POST/v1/business-trips/{id}/approve
Âmbito exigidotrips:decide

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataBusinessTripobrigatório
      Campos de BusinessTrip
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • destinationstringobrigatório
      • purposestringobrigatório
      • departureAtstringobrigatório

        Hora local de Portugal, sem fuso.

      • returnAtstringobrigatório
      • abroadbooleanobrigatório
      • statusstringobrigatório

        Valores:"SUBMITTED""APPROVED""REJECTED""CANCELLED"

      • modestringobrigatório
      • nightsintegerobrigatório
      • legalPercentintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • amountCentsintegerobrigatório
      • exemptAmountCentsintegerobrigatório
      • taxableAmountCentsintegerobrigatório
      • submittedAtstring (date-time)obrigatório
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • rejectionNotestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/business-trips/{id}/approve
Pedido
curl -X POST "https://api.selfcare.work/v1/business-trips/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/approve" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "destination": "string",
    "purpose": "string",
    "departureAt": "2026-10-05T09:00",
    "returnAt": "2026-10-06T18:30",
    "abroad": true,
    "status": "SUBMITTED",
    "mode": "string",
    "nights": 0,
    "legalPercent": 0,
    "currency": "EUR",
    "amountCents": 0,
    "exemptAmountCents": 0,
    "taxableAmountCents": 0,
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "rejectionNote": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Rejeitar uma deslocação

POST/v1/business-trips/{id}/reject

Com a nota obrigatória.

Âmbito exigidotrips:decide

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • notestringobrigatório

Respostas

  • 200OK
    • dataBusinessTripobrigatório
      Campos de BusinessTrip
      • idstring (uuid)obrigatório
      • employeeEmployeeRefobrigatório
        Campos de EmployeeRef
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • destinationstringobrigatório
      • purposestringobrigatório
      • departureAtstringobrigatório

        Hora local de Portugal, sem fuso.

      • returnAtstringobrigatório
      • abroadbooleanobrigatório
      • statusstringobrigatório

        Valores:"SUBMITTED""APPROVED""REJECTED""CANCELLED"

      • modestringobrigatório
      • nightsintegerobrigatório
      • legalPercentintegerobrigatório
      • currencystringobrigatório

        Valores:"EUR"

      • amountCentsintegerobrigatório
      • exemptAmountCentsintegerobrigatório
      • taxableAmountCentsintegerobrigatório
      • submittedAtstring (date-time)obrigatório
      • decidedAtstring (date-time) | nullobrigatóriopode ser null
      • decidedByNamestring | nullobrigatóriopode ser null
      • rejectionNotestring | nullobrigatóriopode ser null
      • paidAtstring (date-time) | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/business-trips/{id}/reject
Pedido
curl -X POST "https://api.selfcare.work/v1/business-trips/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/reject" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "note": "string"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employee": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "destination": "string",
    "purpose": "string",
    "departureAt": "2026-10-05T09:00",
    "returnAt": "2026-10-06T18:30",
    "abroad": true,
    "status": "SUBMITTED",
    "mode": "string",
    "nights": 0,
    "legalPercent": 0,
    "currency": "EUR",
    "amountCents": 0,
    "exemptAmountCents": 0,
    "taxableAmountCents": 0,
    "submittedAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z",
    "decidedByName": "string",
    "rejectionNote": "string",
    "paidAt": "2026-10-16T09:30:00.000Z",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Referência

Documentos

Tipos de documento

GET/v1/document-types

Sem os tipos de saúde nem os disciplinares.

Âmbito exigidodocuments:read

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<DocumentType>obrigatório
      Campos de DocumentType
      • idstring (uuid)obrigatório
      • codestringobrigatório
      • namestringobrigatório
      • scopestringobrigatório

        Valores:"PERSON""TEAM""ORGANIZATION"

      • hasExpirybooleanobrigatório
      • requiresAckbooleanobrigatório
      • systembooleanobrigatório
      • activebooleanobrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/document-types
Pedido
curl "https://api.selfcare.work/v1/document-types" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "PAYSLIP",
      "name": "string",
      "scope": "PERSON",
      "hasExpiry": true,
      "requiresAck": true,
      "system": true,
      "active": true
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Cada documento passa pela mesma regra de acesso da app. Os de saúde e os disciplinares nunca aparecem.

Âmbito exigidodocuments:read

Parâmetros da query

  • employeeId
    string (uuid)
  • spaceId
    string (uuid)
  • typeKey
    string

    Exemplo: PAYSLIP

  • period
    string

    Exemplo: 2026-09

  • expiresBefore
    string (date)

    Exemplo: 2026-10-01

  • updatedSince
    string (date-time)

    Exemplo: 2026-10-01T00:00:00Z

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<Document>obrigatório
      Campos de Document
      • idstring (uuid)obrigatório
      • titlestringobrigatório
      • typeobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • codestringobrigatório
        • namestringobrigatório
      • spaceobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • kindstringobrigatório

          Valores:"PERSON""TEAM""ORGANIZATION"

        • employeeIdstring (uuid) | nullobrigatóriopode ser null
        • teamIdstring (uuid) | nullobrigatóriopode ser null
      • ownerEmployeeIdstring (uuid) | nullobrigatóriopode ser null
      • folderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • visibilitystringobrigatório

        Valores:"PRIVATE""TEAM""SHARED""COMPANY"

      • statusstringobrigatório

        Valores:"PENDING_REVIEW""ACTIVE""REJECTED"

      • originstringobrigatório
      • expiresAtstring (date) | nullobrigatóriopode ser null
      • periodstring | nullobrigatóriopode ser null

        Recibos de vencimento: o mês.

      • ackRequestedbooleanobrigatório
      • ackDueDatestring (date) | nullobrigatóriopode ser null
      • currentVersionDocumentVersion | nullobrigatóriopode ser null
        Campos de DocumentVersion
        • idstring (uuid)obrigatório
        • numberintegerobrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • sha256string | nullobrigatóriopode ser null
        • createdAtstring (date-time)obrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/documents
Pedido
curl "https://api.selfcare.work/v1/documents?typeKey=PAYSLIP&period=2026-09" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "title": "string",
      "type": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "code": "string",
        "name": "string"
      },
      "space": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "kind": "PERSON",
        "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "teamId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
      },
      "ownerEmployeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "folder": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "name": "string"
      },
      "visibility": "PRIVATE",
      "status": "PENDING_REVIEW",
      "origin": "string",
      "expiresAt": "2026-10-16",
      "period": "2026-09",
      "ackRequested": true,
      "ackDueDate": "2026-10-16",
      "currentVersion": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "number": 0,
        "fileName": "string",
        "contentType": "string",
        "sizeBytes": 0,
        "sha256": "string",
        "createdAt": "2026-10-16T09:30:00.000Z"
      },
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Criar um documento

POST/v1/documents

Confirma o upload com o bilhete, confere o sha256, o tamanho e o tipo real. Um recibo de vencimento da mesma pessoa e mês fica como versão nova.

Âmbito exigidodocuments:write

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • ticketstringobrigatório
  • titlestring
  • expiresAtstring | nullpode ser null
  • visibilitystring

    Valores:"PRIVATE""TEAM""SHARED""COMPANY"

  • requestAckboolean

Respostas

  • 201Criado
    • dataDocumentobrigatório
      Campos de Document
      • idstring (uuid)obrigatório
      • titlestringobrigatório
      • typeobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • codestringobrigatório
        • namestringobrigatório
      • spaceobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • kindstringobrigatório

          Valores:"PERSON""TEAM""ORGANIZATION"

        • employeeIdstring (uuid) | nullobrigatóriopode ser null
        • teamIdstring (uuid) | nullobrigatóriopode ser null
      • ownerEmployeeIdstring (uuid) | nullobrigatóriopode ser null
      • folderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • visibilitystringobrigatório

        Valores:"PRIVATE""TEAM""SHARED""COMPANY"

      • statusstringobrigatório

        Valores:"PENDING_REVIEW""ACTIVE""REJECTED"

      • originstringobrigatório
      • expiresAtstring (date) | nullobrigatóriopode ser null
      • periodstring | nullobrigatóriopode ser null

        Recibos de vencimento: o mês.

      • ackRequestedbooleanobrigatório
      • ackDueDatestring (date) | nullobrigatóriopode ser null
      • currentVersionDocumentVersion | nullobrigatóriopode ser null
        Campos de DocumentVersion
        • idstring (uuid)obrigatório
        • numberintegerobrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • sha256string | nullobrigatóriopode ser null
        • createdAtstring (date-time)obrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/documents
Pedido
curl -X POST "https://api.selfcare.work/v1/documents" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "ticket": "string"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "title": "string",
    "type": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string"
    },
    "space": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "kind": "PERSON",
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "teamId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
    },
    "ownerEmployeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "folder": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "name": "string"
    },
    "visibility": "PRIVATE",
    "status": "PENDING_REVIEW",
    "origin": "string",
    "expiresAt": "2026-10-16",
    "period": "2026-09",
    "ackRequested": true,
    "ackDueDate": "2026-10-16",
    "currentVersion": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "number": 0,
      "fileName": "string",
      "contentType": "string",
      "sizeBytes": 0,
      "sha256": "string",
      "createdAt": "2026-10-16T09:30:00.000Z"
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Um documento

GET/v1/documents/{id}
Âmbito exigidodocuments:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataDocumentobrigatório
      Campos de Document
      • idstring (uuid)obrigatório
      • titlestringobrigatório
      • typeobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • codestringobrigatório
        • namestringobrigatório
      • spaceobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • kindstringobrigatório

          Valores:"PERSON""TEAM""ORGANIZATION"

        • employeeIdstring (uuid) | nullobrigatóriopode ser null
        • teamIdstring (uuid) | nullobrigatóriopode ser null
      • ownerEmployeeIdstring (uuid) | nullobrigatóriopode ser null
      • folderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • visibilitystringobrigatório

        Valores:"PRIVATE""TEAM""SHARED""COMPANY"

      • statusstringobrigatório

        Valores:"PENDING_REVIEW""ACTIVE""REJECTED"

      • originstringobrigatório
      • expiresAtstring (date) | nullobrigatóriopode ser null
      • periodstring | nullobrigatóriopode ser null

        Recibos de vencimento: o mês.

      • ackRequestedbooleanobrigatório
      • ackDueDatestring (date) | nullobrigatóriopode ser null
      • currentVersionDocumentVersion | nullobrigatóriopode ser null
        Campos de DocumentVersion
        • idstring (uuid)obrigatório
        • numberintegerobrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • sha256string | nullobrigatóriopode ser null
        • createdAtstring (date-time)obrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/documents/{id}
Pedido
curl "https://api.selfcare.work/v1/documents/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "title": "string",
    "type": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string"
    },
    "space": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "kind": "PERSON",
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "teamId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
    },
    "ownerEmployeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "folder": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "name": "string"
    },
    "visibility": "PRIVATE",
    "status": "PENDING_REVIEW",
    "origin": "string",
    "expiresAt": "2026-10-16",
    "period": "2026-09",
    "ackRequested": true,
    "ackDueDate": "2026-10-16",
    "currentVersion": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "number": 0,
      "fileName": "string",
      "contentType": "string",
      "sizeBytes": 0,
      "sha256": "string",
      "createdAt": "2026-10-16T09:30:00.000Z"
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Editar um documento

PATCH/v1/documents/{id}

Título ou validade, quando a app o deixa. Aceita If-Match.

Âmbito exigidodocuments:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • titlestring
  • expiresAtstring | nullpode ser null

Respostas

  • 200OK
    • dataDocumentobrigatório
      Campos de Document
      • idstring (uuid)obrigatório
      • titlestringobrigatório
      • typeobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • codestringobrigatório
        • namestringobrigatório
      • spaceobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • kindstringobrigatório

          Valores:"PERSON""TEAM""ORGANIZATION"

        • employeeIdstring (uuid) | nullobrigatóriopode ser null
        • teamIdstring (uuid) | nullobrigatóriopode ser null
      • ownerEmployeeIdstring (uuid) | nullobrigatóriopode ser null
      • folderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • visibilitystringobrigatório

        Valores:"PRIVATE""TEAM""SHARED""COMPANY"

      • statusstringobrigatório

        Valores:"PENDING_REVIEW""ACTIVE""REJECTED"

      • originstringobrigatório
      • expiresAtstring (date) | nullobrigatóriopode ser null
      • periodstring | nullobrigatóriopode ser null

        Recibos de vencimento: o mês.

      • ackRequestedbooleanobrigatório
      • ackDueDatestring (date) | nullobrigatóriopode ser null
      • currentVersionDocumentVersion | nullobrigatóriopode ser null
        Campos de DocumentVersion
        • idstring (uuid)obrigatório
        • numberintegerobrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • sha256string | nullobrigatóriopode ser null
        • createdAtstring (date-time)obrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/documents/{id}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/documents/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "title": "string"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "title": "string",
    "type": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string"
    },
    "space": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "kind": "PERSON",
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "teamId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
    },
    "ownerEmployeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "folder": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "name": "string"
    },
    "visibility": "PRIVATE",
    "status": "PENDING_REVIEW",
    "origin": "string",
    "expiresAt": "2026-10-16",
    "period": "2026-09",
    "ackRequested": true,
    "ackDueDate": "2026-10-16",
    "currentVersion": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "number": 0,
      "fileName": "string",
      "contentType": "string",
      "sizeBytes": 0,
      "sha256": "string",
      "createdAt": "2026-10-16T09:30:00.000Z"
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Apagar um documento

DELETE/v1/documents/{id}

Quando a app o deixa. Aceita If-Match.

Âmbito exigidodocuments:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Respostas

  • 200OK
    • dataDeletedobrigatório
      Campos de Deleted
      • idstring (uuid)obrigatório
      • deletedbooleanobrigatório

        Valores:true

    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

DELETEhttps://api.selfcare.work/v1/documents/{id}
Pedido
curl -X DELETE "https://api.selfcare.work/v1/documents/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "deleted": true
  },
  "error": null
}

Versões de um documento

GET/v1/documents/{id}/versions
Âmbito exigidodocuments:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<DocumentVersion>obrigatório
      Campos de DocumentVersion
      • idstring (uuid)obrigatório
      • numberintegerobrigatório
      • fileNamestringobrigatório
      • contentTypestringobrigatório
      • sizeBytesintegerobrigatório
      • sha256string | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/documents/{id}/versions
Pedido
curl "https://api.selfcare.work/v1/documents/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/versions" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "number": 0,
      "fileName": "string",
      "contentType": "string",
      "sizeBytes": 0,
      "sha256": "string",
      "createdAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Carregar uma versão nova

POST/v1/documents/{id}/versions
Âmbito exigidodocuments:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • ticketstringobrigatório
  • expiresAtstring | nullpode ser null

Respostas

  • 201Criado
    • dataDocumentobrigatório
      Campos de Document
      • idstring (uuid)obrigatório
      • titlestringobrigatório
      • typeobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • codestringobrigatório
        • namestringobrigatório
      • spaceobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • kindstringobrigatório

          Valores:"PERSON""TEAM""ORGANIZATION"

        • employeeIdstring (uuid) | nullobrigatóriopode ser null
        • teamIdstring (uuid) | nullobrigatóriopode ser null
      • ownerEmployeeIdstring (uuid) | nullobrigatóriopode ser null
      • folderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • visibilitystringobrigatório

        Valores:"PRIVATE""TEAM""SHARED""COMPANY"

      • statusstringobrigatório

        Valores:"PENDING_REVIEW""ACTIVE""REJECTED"

      • originstringobrigatório
      • expiresAtstring (date) | nullobrigatóriopode ser null
      • periodstring | nullobrigatóriopode ser null

        Recibos de vencimento: o mês.

      • ackRequestedbooleanobrigatório
      • ackDueDatestring (date) | nullobrigatóriopode ser null
      • currentVersionDocumentVersion | nullobrigatóriopode ser null
        Campos de DocumentVersion
        • idstring (uuid)obrigatório
        • numberintegerobrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • sha256string | nullobrigatóriopode ser null
        • createdAtstring (date-time)obrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/documents/{id}/versions
Pedido
curl -X POST "https://api.selfcare.work/v1/documents/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/versions" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "ticket": "string"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "title": "string",
    "type": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string"
    },
    "space": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "kind": "PERSON",
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "teamId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
    },
    "ownerEmployeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "folder": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "name": "string"
    },
    "visibility": "PRIVATE",
    "status": "PENDING_REVIEW",
    "origin": "string",
    "expiresAt": "2026-10-16",
    "period": "2026-09",
    "ackRequested": true,
    "ackDueDate": "2026-10-16",
    "currentVersion": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "number": 0,
      "fileName": "string",
      "contentType": "string",
      "sizeBytes": 0,
      "sha256": "string",
      "createdAt": "2026-10-16T09:30:00.000Z"
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Descarregar uma versão

GET/v1/documents/{id}/versions/{versionId}/content

Devolve um URL pré-assinado válido durante 5 minutos e regista o acesso (origem API).

Âmbito exigidodocuments:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)
  • versionId
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataFileLinkobrigatório
      Campos de FileLink
      • urlstring (uri)obrigatório

        URL pré-assinado de GET, válido 5 minutos. Usar já; não guardar nem partilhar.

      • expiresAtstring (date-time)obrigatório
      • fileNamestringobrigatório
      • contentTypestringobrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/documents/{id}/versions/{versionId}/content
Pedido
curl "https://api.selfcare.work/v1/documents/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/versions/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/content" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "url": "https://example.com",
    "expiresAt": "2026-10-16T09:30:00.000Z",
    "fileName": "string",
    "contentType": "string"
  },
  "error": null
}

Documentos obrigatórios da empresa

GET/v1/document-requirements

Sem os de saúde nem os disciplinares.

Âmbito exigidodocuments:read

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<DocumentRequirement>obrigatório
      Campos de DocumentRequirement
      • idstring (uuid)obrigatório
      • typeobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • codestringobrigatório
        • namestringobrigatório
      • requiresExpirybooleanobrigatório
      • activebooleanobrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/document-requirements
Pedido
curl "https://api.selfcare.work/v1/document-requirements" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "type": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "code": "string",
        "name": "string"
      },
      "requiresExpiry": true,
      "active": true
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Estado dos documentos obrigatórios de um colaborador

GET/v1/employees/{id}/document-checklist

Para o próprio e para quem gere documentos (dono, RH, contabilista). Sem itens de saúde.

Âmbito exigidodocuments:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataarray<DocumentChecklistItem>obrigatório
      Campos de DocumentChecklistItem
      • requirementIdstring (uuid)obrigatório
      • typeobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • codestringobrigatório
        • namestringobrigatório
      • statestringobrigatório

        Valores:"MISSING""PENDING_REVIEW""MISSING_EXPIRY""EXPIRED""EXPIRING""VALID"

      • documentIdstring (uuid) | nullobrigatóriopode ser null
      • expiresAtstring (date) | nullobrigatóriopode ser null
      • daysLeftinteger | nullobrigatóriopode ser null
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/employees/{id}/document-checklist
Pedido
curl "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/document-checklist" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "requirementId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "type": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "code": "string",
        "name": "string"
      },
      "state": "MISSING",
      "documentId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "expiresAt": "2026-10-16",
      "daysLeft": 0
    }
  ],
  "error": null
}

Documento novo, versão nova ou recibo de vencimento (só quem gere documentos). Tipos de saúde e disciplinares são recusados.

Âmbito exigidodocuments:write

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • targetobjectobrigatório
  • fileNamestringobrigatório
  • contentTypestringobrigatório
  • sizeBytesintegerobrigatório
  • sha256stringobrigatório

    sha256 dos bytes, em hexadecimal; confere-se ao confirmar.

Respostas

  • 200OK
    • dataUploadUrlobrigatório
      Campos de UploadUrl
      • urlstringobrigatório

        URL pré-assinado para o PUT dos bytes (15 minutos).

      • methodstringobrigatório

        Valores:"PUT"

      • headersobject<string, string>obrigatório

        Cabeçalhos que o PUT tem de levar.

      • ticketstringobrigatório

        Bilhete para confirmar o upload (30 minutos).

      • expiresAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/uploads
Pedido
curl -X POST "https://api.selfcare.work/v1/uploads" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "target": {
    "kind": "document",
    "typeKey": "CONTRACT",
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "spaceId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "folderId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
  },
  "fileName": "string",
  "contentType": "image/jpeg",
  "sizeBytes": 0,
  "sha256": "string"
}'
Resposta

OK

{
  "data": {
    "url": "string",
    "method": "PUT",
    "headers": {},
    "ticket": "string",
    "expiresAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Confirmar a leitura

POST/v1/documents/{id}/acknowledge

Da versão atual, quando foi pedida.

Âmbito exigidodocuments:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataDocumentobrigatório
      Campos de Document
      • idstring (uuid)obrigatório
      • titlestringobrigatório
      • typeobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • codestringobrigatório
        • namestringobrigatório
      • spaceobjectobrigatório
        Campos
        • idstring (uuid)obrigatório
        • kindstringobrigatório

          Valores:"PERSON""TEAM""ORGANIZATION"

        • employeeIdstring (uuid) | nullobrigatóriopode ser null
        • teamIdstring (uuid) | nullobrigatóriopode ser null
      • ownerEmployeeIdstring (uuid) | nullobrigatóriopode ser null
      • folderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • visibilitystringobrigatório

        Valores:"PRIVATE""TEAM""SHARED""COMPANY"

      • statusstringobrigatório

        Valores:"PENDING_REVIEW""ACTIVE""REJECTED"

      • originstringobrigatório
      • expiresAtstring (date) | nullobrigatóriopode ser null
      • periodstring | nullobrigatóriopode ser null

        Recibos de vencimento: o mês.

      • ackRequestedbooleanobrigatório
      • ackDueDatestring (date) | nullobrigatóriopode ser null
      • currentVersionDocumentVersion | nullobrigatóriopode ser null
        Campos de DocumentVersion
        • idstring (uuid)obrigatório
        • numberintegerobrigatório
        • fileNamestringobrigatório
        • contentTypestringobrigatório
        • sizeBytesintegerobrigatório
        • sha256string | nullobrigatóriopode ser null
        • createdAtstring (date-time)obrigatório
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/documents/{id}/acknowledge
Pedido
curl -X POST "https://api.selfcare.work/v1/documents/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/acknowledge" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "title": "string",
    "type": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "code": "string",
      "name": "string"
    },
    "space": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "kind": "PERSON",
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "teamId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
    },
    "ownerEmployeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "folder": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "name": "string"
    },
    "visibility": "PRIVATE",
    "status": "PENDING_REVIEW",
    "origin": "string",
    "expiresAt": "2026-10-16",
    "period": "2026-09",
    "ackRequested": true,
    "ackDueDate": "2026-10-16",
    "currentVersion": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "number": 0,
      "fileName": "string",
      "contentType": "string",
      "sizeBytes": 0,
      "sha256": "string",
      "createdAt": "2026-10-16T09:30:00.000Z"
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Referência

Colaboradores

Procurar por nif é só para quem vê dados pessoais.

Âmbito exigidoemployees:read

Parâmetros da query

  • q
    string

    Texto a procurar no nome, nome preferido, email de trabalho e número de colaborador (e no email pessoal, para quem vê dados pessoais).

  • status
    string

    Valores separados por vírgulas.

    Exemplo: ACTIVE,ON_LEAVE

  • teamId
    string (uuid)
  • employeeNumber
    string
  • nif
    string
  • updatedSince
    string (date-time)

    Exemplo: 2026-10-01T00:00:00Z

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<Employee>obrigatório
      Campos de Employee
      • idstring (uuid)obrigatório
      • employeeNumberstringobrigatório
      • externalCodestring | nullobrigatóriopode ser null

        Código no ERP; só para quem vê dados pessoais.

      • fullNamestringobrigatório
      • preferredNamestring | nullobrigatóriopode ser null
      • workEmailstring | nullobrigatóriopode ser null
      • workPhonestring | nullobrigatóriopode ser null
      • jobTitlestring | nullobrigatóriopode ser null
      • statusstringobrigatório

        Valores:"ACTIVE""ON_LEAVE""TERMINATED""ARCHIVED"

      • kindstringobrigatório
      • photoUrlstring | nullobrigatóriopode ser null
      • workLocationstring | nullobrigatóriopode ser null
      • locationIdstring (uuid) | nullobrigatóriopode ser null
      • teamsarray<TeamRef>obrigatório
        Campos de TeamRef
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • personalEmailstring | nullobrigatóriopode ser null

        Para colegas, só com o consentimento da pessoa.

      • phonestring | nullobrigatóriopode ser null
      • personalDataobject | nullobrigatóriopode ser null

        Só para dono, RH, contabilista e o próprio; null para os outros.

        Campos
        • dateOfBirthstring (date) | nullobrigatóriopode ser null
        • nationalitystring | nullobrigatóriopode ser null
        • nifstring | nullobrigatóriopode ser null
        • nissstring | nullobrigatóriopode ser null
        • citizenCardNumberstring | nullobrigatóriopode ser null
        • citizenCardExpirystring (date) | nullobrigatóriopode ser null
        • ibanstring | nullobrigatóriopode ser null

          Sempre mascarado (só os últimos 4 dígitos), nas listas e no detalhe.

        • swiftBicstring | nullobrigatóriopode ser null
        • addressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • fiscalAddressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • emergencyContactobjectobrigatório
          Campos
          • namestring | nullobrigatóriopode ser null
          • relationstring | nullobrigatóriopode ser null
          • phonestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/employees
Pedido
curl "https://api.selfcare.work/v1/employees?status=ACTIVE,ON_LEAVE&updatedSince=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "employeeNumber": "string",
      "externalCode": "string",
      "fullName": "string",
      "preferredName": "string",
      "workEmail": "string",
      "workPhone": "string",
      "jobTitle": "string",
      "status": "ACTIVE",
      "kind": "string",
      "photoUrl": "string",
      "workLocation": "string",
      "locationId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "teams": [
        {
          "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
          "name": "string"
        }
      ],
      "personalEmail": "string",
      "phone": "string",
      "personalData": {
        "dateOfBirth": "2026-10-16",
        "nationality": "string",
        "nif": "string",
        "niss": "string",
        "citizenCardNumber": "string",
        "citizenCardExpiry": "2026-10-16",
        "iban": "string",
        "swiftBic": "string",
        "address": {
          "street": "string",
          "city": "string",
          "postalCode": "string",
          "country": "string"
        },
        "fiscalAddress": {
          "street": "string",
          "city": "string",
          "postalCode": "string",
          "country": "string"
        },
        "emergencyContact": {
          "name": "string",
          "relation": "string",
          "phone": "string"
        }
      },
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Criar um colaborador

POST/v1/employees

A mesma ação e a mesma validação do ecrã Novo colaborador (dono, RH ou gestor). Cria o contrato inicial com a data de entrada e, com convite, envia-o por email. O salário não se define aqui: usa POST /employees/{id}/compensation.

Âmbito exigidoemployees:write

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • employeeNumberstring

    Sem ele, o próximo número livre (EMP-0001, EMP-0002...).

  • fullNamestringobrigatório
  • preferredNamestring | nullpode ser null
  • personalEmailstring (email) | nullpode ser null
  • workEmailstring (email) | nullpode ser null
  • phonestring | nullpode ser null
  • kindstring

    Valores:"INTERNAL""EXTERNAL"

  • accessRolestring

    Perfil de acesso da conta. Acima de EMPLOYEE, só o dono e o RH o podem dar.

    Valores:"EMPLOYEE""MANAGER""HR_ADMIN""ACCOUNTANT"

  • contractTypestring

    Valores:"PERMANENT""FIXED_TERM""UNCERTAIN_TERM""INTERNSHIP""SERVICE_PROVIDER"

  • startDatestring (date)obrigatório
  • jobTitlestring | nullpode ser null
  • teamIdsarray<string (uuid)>
  • workLocationstring | nullpode ser null

    Valores:"ON_SITE""HYBRID""REMOTE"

  • externalobject

    Tarifa de um prestador de serviços. É remuneração: pede também o âmbito compensation:write e só dono e RH.

    Campos
    • rateTypestringobrigatório

      Valores:"HOURLY""DAILY""MONTHLY"

    • rateAmountCentsintegerobrigatório
    • unitsPerMonthnumber | nullpode ser null
    • vacationPaidboolean | nullpode ser null
    • absencesPaidboolean | nullpode ser null
    • monthsPaidPerYearinteger | nullpode ser null
  • sendInviteboolean

    Envia o convite para o email pessoal; o email profissional passa a ser o de entrada na app.

Respostas

  • 201Criado
    • dataEmployeeobrigatório
      Campos de Employee
      • idstring (uuid)obrigatório
      • employeeNumberstringobrigatório
      • externalCodestring | nullobrigatóriopode ser null

        Código no ERP; só para quem vê dados pessoais.

      • fullNamestringobrigatório
      • preferredNamestring | nullobrigatóriopode ser null
      • workEmailstring | nullobrigatóriopode ser null
      • workPhonestring | nullobrigatóriopode ser null
      • jobTitlestring | nullobrigatóriopode ser null
      • statusstringobrigatório

        Valores:"ACTIVE""ON_LEAVE""TERMINATED""ARCHIVED"

      • kindstringobrigatório
      • photoUrlstring | nullobrigatóriopode ser null
      • workLocationstring | nullobrigatóriopode ser null
      • locationIdstring (uuid) | nullobrigatóriopode ser null
      • teamsarray<TeamRef>obrigatório
        Campos de TeamRef
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • personalEmailstring | nullobrigatóriopode ser null

        Para colegas, só com o consentimento da pessoa.

      • phonestring | nullobrigatóriopode ser null
      • personalDataobject | nullobrigatóriopode ser null

        Só para dono, RH, contabilista e o próprio; null para os outros.

        Campos
        • dateOfBirthstring (date) | nullobrigatóriopode ser null
        • nationalitystring | nullobrigatóriopode ser null
        • nifstring | nullobrigatóriopode ser null
        • nissstring | nullobrigatóriopode ser null
        • citizenCardNumberstring | nullobrigatóriopode ser null
        • citizenCardExpirystring (date) | nullobrigatóriopode ser null
        • ibanstring | nullobrigatóriopode ser null

          Sempre mascarado (só os últimos 4 dígitos), nas listas e no detalhe.

        • swiftBicstring | nullobrigatóriopode ser null
        • addressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • fiscalAddressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • emergencyContactobjectobrigatório
          Campos
          • namestring | nullobrigatóriopode ser null
          • relationstring | nullobrigatóriopode ser null
          • phonestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/employees
Pedido
curl -X POST "https://api.selfcare.work/v1/employees" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "fullName": "string",
  "startDate": "2026-11-01"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeNumber": "string",
    "externalCode": "string",
    "fullName": "string",
    "preferredName": "string",
    "workEmail": "string",
    "workPhone": "string",
    "jobTitle": "string",
    "status": "ACTIVE",
    "kind": "string",
    "photoUrl": "string",
    "workLocation": "string",
    "locationId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "teams": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "name": "string"
      }
    ],
    "personalEmail": "string",
    "phone": "string",
    "personalData": {
      "dateOfBirth": "2026-10-16",
      "nationality": "string",
      "nif": "string",
      "niss": "string",
      "citizenCardNumber": "string",
      "citizenCardExpiry": "2026-10-16",
      "iban": "string",
      "swiftBic": "string",
      "address": {
        "street": "string",
        "city": "string",
        "postalCode": "string",
        "country": "string"
      },
      "fiscalAddress": {
        "street": "string",
        "city": "string",
        "postalCode": "string",
        "country": "string"
      },
      "emergencyContact": {
        "name": "string",
        "relation": "string",
        "phone": "string"
      }
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Um colaborador

GET/v1/employees/{id}
Âmbito exigidoemployees:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataEmployeeobrigatório
      Campos de Employee
      • idstring (uuid)obrigatório
      • employeeNumberstringobrigatório
      • externalCodestring | nullobrigatóriopode ser null

        Código no ERP; só para quem vê dados pessoais.

      • fullNamestringobrigatório
      • preferredNamestring | nullobrigatóriopode ser null
      • workEmailstring | nullobrigatóriopode ser null
      • workPhonestring | nullobrigatóriopode ser null
      • jobTitlestring | nullobrigatóriopode ser null
      • statusstringobrigatório

        Valores:"ACTIVE""ON_LEAVE""TERMINATED""ARCHIVED"

      • kindstringobrigatório
      • photoUrlstring | nullobrigatóriopode ser null
      • workLocationstring | nullobrigatóriopode ser null
      • locationIdstring (uuid) | nullobrigatóriopode ser null
      • teamsarray<TeamRef>obrigatório
        Campos de TeamRef
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • personalEmailstring | nullobrigatóriopode ser null

        Para colegas, só com o consentimento da pessoa.

      • phonestring | nullobrigatóriopode ser null
      • personalDataobject | nullobrigatóriopode ser null

        Só para dono, RH, contabilista e o próprio; null para os outros.

        Campos
        • dateOfBirthstring (date) | nullobrigatóriopode ser null
        • nationalitystring | nullobrigatóriopode ser null
        • nifstring | nullobrigatóriopode ser null
        • nissstring | nullobrigatóriopode ser null
        • citizenCardNumberstring | nullobrigatóriopode ser null
        • citizenCardExpirystring (date) | nullobrigatóriopode ser null
        • ibanstring | nullobrigatóriopode ser null

          Sempre mascarado (só os últimos 4 dígitos), nas listas e no detalhe.

        • swiftBicstring | nullobrigatóriopode ser null
        • addressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • fiscalAddressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • emergencyContactobjectobrigatório
          Campos
          • namestring | nullobrigatóriopode ser null
          • relationstring | nullobrigatóriopode ser null
          • phonestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/employees/{id}
Pedido
curl "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeNumber": "string",
    "externalCode": "string",
    "fullName": "string",
    "preferredName": "string",
    "workEmail": "string",
    "workPhone": "string",
    "jobTitle": "string",
    "status": "ACTIVE",
    "kind": "string",
    "photoUrl": "string",
    "workLocation": "string",
    "locationId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "teams": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "name": "string"
      }
    ],
    "personalEmail": "string",
    "phone": "string",
    "personalData": {
      "dateOfBirth": "2026-10-16",
      "nationality": "string",
      "nif": "string",
      "niss": "string",
      "citizenCardNumber": "string",
      "citizenCardExpiry": "2026-10-16",
      "iban": "string",
      "swiftBic": "string",
      "address": {
        "street": "string",
        "city": "string",
        "postalCode": "string",
        "country": "string"
      },
      "fiscalAddress": {
        "street": "string",
        "city": "string",
        "postalCode": "string",
        "country": "string"
      },
      "emergencyContact": {
        "name": "string",
        "relation": "string",
        "phone": "string"
      }
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Editar a ficha de um colaborador

PATCH/v1/employees/{id}

Muda só os campos enviados, pelas regras da ficha na app. Quem não pode escrever nada na pessoa recebe 404. If-Match opcional (412 se a ficha mudou).

Âmbito exigidoemployees:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • fullNamestring
  • preferredNamestring | nullpode ser null
  • personalEmailstring (email) | nullpode ser null
  • phonestring | nullpode ser null
  • genderstring | nullpode ser null

    Valores:"FEMALE""MALE""OTHER"

  • dateOfBirthstring (date) | nullpode ser null
  • nationalitystring | nullpode ser null
  • nifstring | nullpode ser null
  • nissstring | nullpode ser null
  • citizenCardNumberstring | nullpode ser null
  • citizenCardExpirystring (date) | nullpode ser null
  • addressobject
    Campos
    • streetstring | nullpode ser null
    • citystring | nullpode ser null
    • postalCodestring | nullpode ser null
    • countrystring | nullpode ser null
  • emergencyContactobject
    Campos
    • namestring | nullpode ser null
    • relationstring | nullpode ser null
    • phonestring | nullpode ser null
  • jobTitlestring | nullpode ser null
  • workEmailstring (email) | nullpode ser null
  • workPhonestring | nullpode ser null
  • externalCodestring | nullpode ser null
  • locationIdstring (uuid) | nullpode ser null
  • teamIdsarray<string (uuid)>

Respostas

  • 200OK
    • dataEmployeeobrigatório
      Campos de Employee
      • idstring (uuid)obrigatório
      • employeeNumberstringobrigatório
      • externalCodestring | nullobrigatóriopode ser null

        Código no ERP; só para quem vê dados pessoais.

      • fullNamestringobrigatório
      • preferredNamestring | nullobrigatóriopode ser null
      • workEmailstring | nullobrigatóriopode ser null
      • workPhonestring | nullobrigatóriopode ser null
      • jobTitlestring | nullobrigatóriopode ser null
      • statusstringobrigatório

        Valores:"ACTIVE""ON_LEAVE""TERMINATED""ARCHIVED"

      • kindstringobrigatório
      • photoUrlstring | nullobrigatóriopode ser null
      • workLocationstring | nullobrigatóriopode ser null
      • locationIdstring (uuid) | nullobrigatóriopode ser null
      • teamsarray<TeamRef>obrigatório
        Campos de TeamRef
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • personalEmailstring | nullobrigatóriopode ser null

        Para colegas, só com o consentimento da pessoa.

      • phonestring | nullobrigatóriopode ser null
      • personalDataobject | nullobrigatóriopode ser null

        Só para dono, RH, contabilista e o próprio; null para os outros.

        Campos
        • dateOfBirthstring (date) | nullobrigatóriopode ser null
        • nationalitystring | nullobrigatóriopode ser null
        • nifstring | nullobrigatóriopode ser null
        • nissstring | nullobrigatóriopode ser null
        • citizenCardNumberstring | nullobrigatóriopode ser null
        • citizenCardExpirystring (date) | nullobrigatóriopode ser null
        • ibanstring | nullobrigatóriopode ser null

          Sempre mascarado (só os últimos 4 dígitos), nas listas e no detalhe.

        • swiftBicstring | nullobrigatóriopode ser null
        • addressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • fiscalAddressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • emergencyContactobjectobrigatório
          Campos
          • namestring | nullobrigatóriopode ser null
          • relationstring | nullobrigatóriopode ser null
          • phonestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/employees/{id}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "dateOfBirth": "2026-11-01",
  "citizenCardExpiry": "2026-11-01"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeNumber": "string",
    "externalCode": "string",
    "fullName": "string",
    "preferredName": "string",
    "workEmail": "string",
    "workPhone": "string",
    "jobTitle": "string",
    "status": "ACTIVE",
    "kind": "string",
    "photoUrl": "string",
    "workLocation": "string",
    "locationId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "teams": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "name": "string"
      }
    ],
    "personalEmail": "string",
    "phone": "string",
    "personalData": {
      "dateOfBirth": "2026-10-16",
      "nationality": "string",
      "nif": "string",
      "niss": "string",
      "citizenCardNumber": "string",
      "citizenCardExpiry": "2026-10-16",
      "iban": "string",
      "swiftBic": "string",
      "address": {
        "street": "string",
        "city": "string",
        "postalCode": "string",
        "country": "string"
      },
      "fiscalAddress": {
        "street": "string",
        "city": "string",
        "postalCode": "string",
        "country": "string"
      },
      "emergencyContact": {
        "name": "string",
        "relation": "string",
        "phone": "string"
      }
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Histórico de contratos de um colaborador

GET/v1/employees/{id}/contracts

Âmbito sensível, marcado à parte. Para dono, RH, contabilista e o próprio.

Âmbito exigidocontracts:readsensível

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<Contract>obrigatório
      Campos de Contract
      • idstring (uuid)obrigatório
      • employeeIdstring (uuid)obrigatório
      • effectiveFromstring (date)obrigatório
      • effectiveTostring (date) | nullobrigatóriopode ser null
      • reasonstringobrigatório

        Valores:"HIRING""RENEWAL""CONVERSION_TO_PERMANENT""CONVERSION_FROM_SERVICE_PROVIDER""CHANGE_OF_HOURS""OTHER"

      • contractTypestringobrigatório

        Valores:"PERMANENT""FIXED_TERM""UNCERTAIN_TERM""INTERNSHIP""SERVICE_PROVIDER"

      • startDatestring (date)obrigatório
      • endDatestring (date) | nullobrigatóriopode ser null
      • probationEndDatestring (date) | nullobrigatóriopode ser null
      • weeklyHoursnumberobrigatório
      • vacationDaysPerYearintegerobrigatório
      • cctsstring | nullobrigatóriopode ser null
      • notesstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/employees/{id}/contracts
Pedido
curl "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/contracts" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "effectiveFrom": "2026-10-16",
      "effectiveTo": "2026-10-16",
      "reason": "HIRING",
      "contractType": "PERMANENT",
      "startDate": "2026-10-16",
      "endDate": "2026-10-16",
      "probationEndDate": "2026-10-16",
      "weeklyHours": 0,
      "vacationDaysPerYear": 0,
      "ccts": "string",
      "notes": "string",
      "createdAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Registar uma alteração de contrato

POST/v1/employees/{id}/contracts

Âmbito contracts:write, só dono e RH. Motivo obrigatório e Idempotency-Key obrigatória. A entrada vigente fecha na véspera da data de efeito, que tem de ser posterior. Fica no registo de auditoria com o antes e o depois, e o colaborador é avisado.

Âmbito exigidocontracts:writesensível

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    obrigatório
    string

    Obrigatória nesta escrita (428 sem ela). Durante 24 horas, repetir com a mesma chave e o mesmo corpo devolve a mesma resposta; com outro corpo, 422.

Corpo do pedido

application/json
  • effectiveFromstring (date)obrigatório
  • reasonstringobrigatório

    Valores:"HIRING""RENEWAL""CONVERSION_TO_PERMANENT""CONVERSION_FROM_SERVICE_PROVIDER""CHANGE_OF_HOURS""OTHER"

  • contractTypestringobrigatório

    Valores:"PERMANENT""FIXED_TERM""UNCERTAIN_TERM""INTERNSHIP""SERVICE_PROVIDER"

  • startDatestring (date)obrigatório
  • endDatestring (date) | nullpode ser null
  • probationEndDatestring (date) | nullpode ser null
  • weeklyHoursnumberobrigatório
  • vacationDaysPerYearinteger
  • cctsstring | nullpode ser null
  • notesstring | nullpode ser null
  • justificationstringobrigatório

    Motivo da alteração, obrigatório. Fica no registo de auditoria com os valores antes e depois.

Respostas

  • 201Criado
    • dataContractobrigatório
      Campos de Contract
      • idstring (uuid)obrigatório
      • employeeIdstring (uuid)obrigatório
      • effectiveFromstring (date)obrigatório
      • effectiveTostring (date) | nullobrigatóriopode ser null
      • reasonstringobrigatório

        Valores:"HIRING""RENEWAL""CONVERSION_TO_PERMANENT""CONVERSION_FROM_SERVICE_PROVIDER""CHANGE_OF_HOURS""OTHER"

      • contractTypestringobrigatório

        Valores:"PERMANENT""FIXED_TERM""UNCERTAIN_TERM""INTERNSHIP""SERVICE_PROVIDER"

      • startDatestring (date)obrigatório
      • endDatestring (date) | nullobrigatóriopode ser null
      • probationEndDatestring (date) | nullobrigatóriopode ser null
      • weeklyHoursnumberobrigatório
      • vacationDaysPerYearintegerobrigatório
      • cctsstring | nullobrigatóriopode ser null
      • notesstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 428Falta o If-Match ou a Idempotency-Key, obrigatórios nesta escrita
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/employees/{id}/contracts
Pedido
curl -X POST "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/contracts" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "effectiveFrom": "2026-11-01",
  "reason": "HIRING",
  "contractType": "PERMANENT",
  "startDate": "2026-11-01",
  "endDate": "2026-11-01",
  "probationEndDate": "2026-11-01",
  "weeklyHours": 0,
  "justification": "Revisão salarial anual aprovada pela administração"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "effectiveFrom": "2026-10-16",
    "effectiveTo": "2026-10-16",
    "reason": "HIRING",
    "contractType": "PERMANENT",
    "startDate": "2026-10-16",
    "endDate": "2026-10-16",
    "probationEndDate": "2026-10-16",
    "weeklyHours": 0,
    "vacationDaysPerYear": 0,
    "ccts": "string",
    "notes": "string",
    "createdAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Histórico de remuneração de um colaborador

GET/v1/employees/{id}/compensation

Âmbito sensível, marcado à parte. Para dono, RH, contabilista e o próprio. Não é processamento salarial.

Âmbito exigidocompensation:readsensível

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<Compensation>obrigatório
      Campos de Compensation
      • idstring (uuid)obrigatório
      • employeeIdstring (uuid)obrigatório
      • effectiveFromstring (date)obrigatório
      • effectiveTostring (date) | nullobrigatóriopode ser null
      • reasonstringobrigatório

        Valores:"HIRING""RAISE""PROMOTION""ADJUSTMENT""ROLE_CHANGE""BENEFITS_REVIEW"

      • currencystringobrigatório

        Valores:"EUR"

      • baseSalaryMonthlyCentsintegerobrigatório
      • salaryPaidMonthsintegerobrigatório
      • mealAllowanceDailyCentsintegerobrigatório
      • mealAllowanceFormstringobrigatório
      • seniorityBonusCentsintegerobrigatório
      • coverflexMonthlyCentsinteger | nullobrigatóriopode ser null
      • expenseAllowanceCentsinteger | nullobrigatóriopode ser null
      • workingTimeExemptionobjectobrigatório

        Isenção de horário de trabalho (CT arts. 218.º e 219.º).

        Campos
        • enabledbooleanobrigatório
        • modalitystring | nullobrigatóriopode ser null

          Valores:"NOT_SUBJECT_TO_PNT_LIMITS""EXTENDED_DAILY_OR_WEEKLY""PNT_OBSERVED"

        • compensationModestring | nullobrigatóriopode ser null

          Valores:"PERCENT_OF_BASE""HOURS_PER_DAY""HOURS_PER_WEEK""FIXED_MONTHLY"

        • compensationValuenumber | nullobrigatóriopode ser null

          Percentagem do vencimento base, horas ou euros por mês, conforme o modo de cálculo.

        • agreementDatestring (date) | nullobrigatóriopode ser null
      • externalobject | nullobrigatóriopode ser null
        Campos
        • rateTypestring | nullobrigatóriopode ser null
        • rateAmountCentsinteger | nullobrigatóriopode ser null
        • unitsPerMonthnumber | nullobrigatóriopode ser null
        • vacationPaidboolean | nullobrigatóriopode ser null
        • absencesPaidboolean | nullobrigatóriopode ser null
        • monthsPaidPerYearinteger | nullobrigatóriopode ser null
      • notesstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/employees/{id}/compensation
Pedido
curl "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/compensation" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "effectiveFrom": "2026-10-16",
      "effectiveTo": "2026-10-16",
      "reason": "HIRING",
      "currency": "EUR",
      "baseSalaryMonthlyCents": 0,
      "salaryPaidMonths": 0,
      "mealAllowanceDailyCents": 0,
      "mealAllowanceForm": "string",
      "seniorityBonusCents": 0,
      "coverflexMonthlyCents": 0,
      "expenseAllowanceCents": 0,
      "workingTimeExemption": {
        "enabled": true,
        "modality": "NOT_SUBJECT_TO_PNT_LIMITS",
        "compensationMode": "PERCENT_OF_BASE",
        "compensationValue": 0,
        "agreementDate": "2026-10-16"
      },
      "external": {
        "rateType": "string",
        "rateAmountCents": 0,
        "unitsPerMonth": 0,
        "vacationPaid": true,
        "absencesPaid": true,
        "monthsPaidPerYear": 0
      },
      "notes": "string",
      "createdAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Registar uma alteração de remuneração

POST/v1/employees/{id}/compensation

Âmbito compensation:write, só dono e RH. Motivo obrigatório e Idempotency-Key obrigatória. O subsídio de alimentação é sempre o da empresa. Fica no registo de auditoria com o antes e o depois; o colaborador é avisado na app e por email, sem valores.

Âmbito exigidocompensation:writesensível

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    obrigatório
    string

    Obrigatória nesta escrita (428 sem ela). Durante 24 horas, repetir com a mesma chave e o mesmo corpo devolve a mesma resposta; com outro corpo, 422.

Corpo do pedido

application/json
  • effectiveFromstring (date)obrigatório
  • reasonstringobrigatório

    Valores:"HIRING""RAISE""PROMOTION""ADJUSTMENT""ROLE_CHANGE""BENEFITS_REVIEW"

  • baseSalaryMonthlyCentsintegerobrigatório
  • salaryPaidMonthsnumberobrigatório
  • seniorityBonusCentsinteger
  • coverflexMonthlyCentsinteger | nullpode ser null
  • expenseAllowanceCentsinteger | nullpode ser null
  • workingTimeExemptionobject

    Isenção de horário de trabalho (CT arts. 218.º e 219.º).

    Campos
    • enabledbooleanobrigatório
    • modalitystring | nullpode ser null

      Valores:"NOT_SUBJECT_TO_PNT_LIMITS""EXTENDED_DAILY_OR_WEEKLY""PNT_OBSERVED"

    • compensationModestring | nullpode ser null

      Valores:"PERCENT_OF_BASE""HOURS_PER_DAY""HOURS_PER_WEEK""FIXED_MONTHLY"

    • compensationValuenumber | nullpode ser null

      Percentagem do vencimento base, horas ou euros por mês, conforme o modo de cálculo.

    • agreementDatestring (date) | nullpode ser null
  • notesstring | nullpode ser null
  • justificationstringobrigatório

    Motivo da alteração, obrigatório. Fica no registo de auditoria com os valores antes e depois.

Respostas

  • 201Criado
    • dataCompensationobrigatório
      Campos de Compensation
      • idstring (uuid)obrigatório
      • employeeIdstring (uuid)obrigatório
      • effectiveFromstring (date)obrigatório
      • effectiveTostring (date) | nullobrigatóriopode ser null
      • reasonstringobrigatório

        Valores:"HIRING""RAISE""PROMOTION""ADJUSTMENT""ROLE_CHANGE""BENEFITS_REVIEW"

      • currencystringobrigatório

        Valores:"EUR"

      • baseSalaryMonthlyCentsintegerobrigatório
      • salaryPaidMonthsintegerobrigatório
      • mealAllowanceDailyCentsintegerobrigatório
      • mealAllowanceFormstringobrigatório
      • seniorityBonusCentsintegerobrigatório
      • coverflexMonthlyCentsinteger | nullobrigatóriopode ser null
      • expenseAllowanceCentsinteger | nullobrigatóriopode ser null
      • workingTimeExemptionobjectobrigatório

        Isenção de horário de trabalho (CT arts. 218.º e 219.º).

        Campos
        • enabledbooleanobrigatório
        • modalitystring | nullobrigatóriopode ser null

          Valores:"NOT_SUBJECT_TO_PNT_LIMITS""EXTENDED_DAILY_OR_WEEKLY""PNT_OBSERVED"

        • compensationModestring | nullobrigatóriopode ser null

          Valores:"PERCENT_OF_BASE""HOURS_PER_DAY""HOURS_PER_WEEK""FIXED_MONTHLY"

        • compensationValuenumber | nullobrigatóriopode ser null

          Percentagem do vencimento base, horas ou euros por mês, conforme o modo de cálculo.

        • agreementDatestring (date) | nullobrigatóriopode ser null
      • externalobject | nullobrigatóriopode ser null
        Campos
        • rateTypestring | nullobrigatóriopode ser null
        • rateAmountCentsinteger | nullobrigatóriopode ser null
        • unitsPerMonthnumber | nullobrigatóriopode ser null
        • vacationPaidboolean | nullobrigatóriopode ser null
        • absencesPaidboolean | nullobrigatóriopode ser null
        • monthsPaidPerYearinteger | nullobrigatóriopode ser null
      • notesstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 428Falta o If-Match ou a Idempotency-Key, obrigatórios nesta escrita
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/employees/{id}/compensation
Pedido
curl -X POST "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/compensation" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "effectiveFrom": "2026-11-01",
  "reason": "HIRING",
  "baseSalaryMonthlyCents": 0,
  "salaryPaidMonths": 12,
  "justification": "Revisão salarial anual aprovada pela administração"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "effectiveFrom": "2026-10-16",
    "effectiveTo": "2026-10-16",
    "reason": "HIRING",
    "currency": "EUR",
    "baseSalaryMonthlyCents": 0,
    "salaryPaidMonths": 0,
    "mealAllowanceDailyCents": 0,
    "mealAllowanceForm": "string",
    "seniorityBonusCents": 0,
    "coverflexMonthlyCents": 0,
    "expenseAllowanceCents": 0,
    "workingTimeExemption": {
      "enabled": true,
      "modality": "NOT_SUBJECT_TO_PNT_LIMITS",
      "compensationMode": "PERCENT_OF_BASE",
      "compensationValue": 0,
      "agreementDate": "2026-10-16"
    },
    "external": {
      "rateType": "string",
      "rateAmountCents": 0,
      "unitsPerMonth": 0,
      "vacationPaid": true,
      "absencesPaid": true,
      "monthsPaidPerYear": 0
    },
    "notes": "string",
    "createdAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Mudança de IBAN à espera de confirmação

GET/v1/employees/{id}/bank-details/pending

Para quem vê os dados pessoais da pessoa (dono, RH, contabilista e o próprio). A mudança só entra em vigor quando o colaborador a confirma na app. data é null quando não há nenhuma pendente.

Âmbito exigidoemployees:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataPendingBankDetailsChange | nullobrigatóriopode ser null
      Campos de PendingBankDetailsChange
      • idstring (uuid)obrigatório
      • employeeIdstring (uuid)obrigatório
      • statusstringobrigatório

        PENDING até o colaborador decidir. CONFIRMED aplica o IBAN; REJECTED, CANCELLED e EXPIRED deixam o anterior.

        Valores:"PENDING""CONFIRMED""REJECTED""EXPIRED""CANCELLED"

      • ibanMaskedstringobrigatório

        Mascarado: só os últimos 4 dígitos.

      • requestedAtstring (date-time)obrigatório
      • expiresAtstring (date-time)obrigatório

        Até quando o colaborador pode confirmar. Nunca entra em vigor sozinha.

      • decidedAtstring (date-time) | nullobrigatóriopode ser null
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/employees/{id}/bank-details/pending
Pedido
curl "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/bank-details/pending" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "status": "PENDING",
    "ibanMasked": "****0154",
    "requestedAt": "2026-10-16T09:30:00.000Z",
    "expiresAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Cancelar a mudança de IBAN pendente

DELETE/v1/employees/{id}/bank-details/pending

O próprio, o dono ou o RH. O IBAN da ficha não muda. Sem pedido pendente, 404.

Âmbito exigidobank-details:writesensível

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Respostas

  • 200OK
    • dataPendingBankDetailsChangeobrigatório
      Campos de PendingBankDetailsChange
      • idstring (uuid)obrigatório
      • employeeIdstring (uuid)obrigatório
      • statusstringobrigatório

        PENDING até o colaborador decidir. CONFIRMED aplica o IBAN; REJECTED, CANCELLED e EXPIRED deixam o anterior.

        Valores:"PENDING""CONFIRMED""REJECTED""EXPIRED""CANCELLED"

      • ibanMaskedstringobrigatório

        Mascarado: só os últimos 4 dígitos.

      • requestedAtstring (date-time)obrigatório
      • expiresAtstring (date-time)obrigatório

        Até quando o colaborador pode confirmar. Nunca entra em vigor sozinha.

      • decidedAtstring (date-time) | nullobrigatóriopode ser null
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

DELETEhttps://api.selfcare.work/v1/employees/{id}/bank-details/pending
Pedido
curl -X DELETE "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/bank-details/pending" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "status": "PENDING",
    "ibanMasked": "****0154",
    "requestedAt": "2026-10-16T09:30:00.000Z",
    "expiresAt": "2026-10-16T09:30:00.000Z",
    "decidedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Arquivar um colaborador

POST/v1/employees/{id}/archive

Dono e RH. Tira a pessoa das listas e dos seletores sem cessar o contrato, como na app. Um colaborador cessado ou já arquivado dá 409.

Âmbito exigidoemployees:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataEmployeeobrigatório
      Campos de Employee
      • idstring (uuid)obrigatório
      • employeeNumberstringobrigatório
      • externalCodestring | nullobrigatóriopode ser null

        Código no ERP; só para quem vê dados pessoais.

      • fullNamestringobrigatório
      • preferredNamestring | nullobrigatóriopode ser null
      • workEmailstring | nullobrigatóriopode ser null
      • workPhonestring | nullobrigatóriopode ser null
      • jobTitlestring | nullobrigatóriopode ser null
      • statusstringobrigatório

        Valores:"ACTIVE""ON_LEAVE""TERMINATED""ARCHIVED"

      • kindstringobrigatório
      • photoUrlstring | nullobrigatóriopode ser null
      • workLocationstring | nullobrigatóriopode ser null
      • locationIdstring (uuid) | nullobrigatóriopode ser null
      • teamsarray<TeamRef>obrigatório
        Campos de TeamRef
        • idstring (uuid)obrigatório
        • namestringobrigatório
      • personalEmailstring | nullobrigatóriopode ser null

        Para colegas, só com o consentimento da pessoa.

      • phonestring | nullobrigatóriopode ser null
      • personalDataobject | nullobrigatóriopode ser null

        Só para dono, RH, contabilista e o próprio; null para os outros.

        Campos
        • dateOfBirthstring (date) | nullobrigatóriopode ser null
        • nationalitystring | nullobrigatóriopode ser null
        • nifstring | nullobrigatóriopode ser null
        • nissstring | nullobrigatóriopode ser null
        • citizenCardNumberstring | nullobrigatóriopode ser null
        • citizenCardExpirystring (date) | nullobrigatóriopode ser null
        • ibanstring | nullobrigatóriopode ser null

          Sempre mascarado (só os últimos 4 dígitos), nas listas e no detalhe.

        • swiftBicstring | nullobrigatóriopode ser null
        • addressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • fiscalAddressobjectobrigatório
          Campos
          • streetstring | nullobrigatóriopode ser null
          • citystring | nullobrigatóriopode ser null
          • postalCodestring | nullobrigatóriopode ser null
          • countrystring | nullpode ser null
        • emergencyContactobjectobrigatório
          Campos
          • namestring | nullobrigatóriopode ser null
          • relationstring | nullobrigatóriopode ser null
          • phonestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/employees/{id}/archive
Pedido
curl -X POST "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/archive" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeNumber": "string",
    "externalCode": "string",
    "fullName": "string",
    "preferredName": "string",
    "workEmail": "string",
    "workPhone": "string",
    "jobTitle": "string",
    "status": "ACTIVE",
    "kind": "string",
    "photoUrl": "string",
    "workLocation": "string",
    "locationId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "teams": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "name": "string"
      }
    ],
    "personalEmail": "string",
    "phone": "string",
    "personalData": {
      "dateOfBirth": "2026-10-16",
      "nationality": "string",
      "nif": "string",
      "niss": "string",
      "citizenCardNumber": "string",
      "citizenCardExpiry": "2026-10-16",
      "iban": "string",
      "swiftBic": "string",
      "address": {
        "street": "string",
        "city": "string",
        "postalCode": "string",
        "country": "string"
      },
      "fiscalAddress": {
        "street": "string",
        "city": "string",
        "postalCode": "string",
        "country": "string"
      },
      "emergencyContact": {
        "name": "string",
        "relation": "string",
        "phone": "string"
      }
    },
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Corrigir uma entrada de contrato

PATCH/v1/employees/{id}/contracts/{contractId}

Âmbito contracts:write, só dono e RH. If-Match e motivo obrigatórios. Como na app, só se corrige enquanto não há recibos emitidos (ou num prestador); depois disso regista-se uma entrada nova.

Âmbito exigidocontracts:writesensível

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)
  • contractId
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    obrigatório
    string

    Obrigatório nesta escrita (428 sem ele): o ETag que leste no GET. Se o recurso mudou entretanto, 412. O * não serve.

Corpo do pedido

application/json
  • effectiveFromstring (date)
  • reasonstring

    Valores:"HIRING""RENEWAL""CONVERSION_TO_PERMANENT""CONVERSION_FROM_SERVICE_PROVIDER""CHANGE_OF_HOURS""OTHER"

  • contractTypestring

    Valores:"PERMANENT""FIXED_TERM""UNCERTAIN_TERM""INTERNSHIP""SERVICE_PROVIDER"

  • startDatestring (date)
  • endDatestring (date) | nullpode ser null
  • probationEndDatestring (date) | nullpode ser null
  • weeklyHoursnumber
  • vacationDaysPerYearinteger
  • cctsstring | nullpode ser null
  • notesstring | nullpode ser null
  • justificationstringobrigatório

    Motivo da alteração, obrigatório. Fica no registo de auditoria com os valores antes e depois.

Respostas

  • 200OK
    • dataContractobrigatório
      Campos de Contract
      • idstring (uuid)obrigatório
      • employeeIdstring (uuid)obrigatório
      • effectiveFromstring (date)obrigatório
      • effectiveTostring (date) | nullobrigatóriopode ser null
      • reasonstringobrigatório

        Valores:"HIRING""RENEWAL""CONVERSION_TO_PERMANENT""CONVERSION_FROM_SERVICE_PROVIDER""CHANGE_OF_HOURS""OTHER"

      • contractTypestringobrigatório

        Valores:"PERMANENT""FIXED_TERM""UNCERTAIN_TERM""INTERNSHIP""SERVICE_PROVIDER"

      • startDatestring (date)obrigatório
      • endDatestring (date) | nullobrigatóriopode ser null
      • probationEndDatestring (date) | nullobrigatóriopode ser null
      • weeklyHoursnumberobrigatório
      • vacationDaysPerYearintegerobrigatório
      • cctsstring | nullobrigatóriopode ser null
      • notesstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 428Falta o If-Match ou a Idempotency-Key, obrigatórios nesta escrita
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/employees/{id}/contracts/{contractId}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/contracts/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -H 'If-Match: W/"…"' \
  -d '{
  "effectiveFrom": "2026-11-01",
  "startDate": "2026-11-01",
  "endDate": "2026-11-01",
  "probationEndDate": "2026-11-01",
  "justification": "Revisão salarial anual aprovada pela administração"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "effectiveFrom": "2026-10-16",
    "effectiveTo": "2026-10-16",
    "reason": "HIRING",
    "contractType": "PERMANENT",
    "startDate": "2026-10-16",
    "endDate": "2026-10-16",
    "probationEndDate": "2026-10-16",
    "weeklyHours": 0,
    "vacationDaysPerYear": 0,
    "ccts": "string",
    "notes": "string",
    "createdAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Corrigir uma entrada de remuneração

PATCH/v1/employees/{id}/compensation/{entryId}

Âmbito compensation:write, só dono e RH. If-Match e motivo obrigatórios. Muda só os campos enviados; o colaborador é avisado.

Âmbito exigidocompensation:writesensível

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)
  • entryId
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    obrigatório
    string

    Obrigatório nesta escrita (428 sem ele): o ETag que leste no GET. Se o recurso mudou entretanto, 412. O * não serve.

Corpo do pedido

application/json
  • effectiveFromstring (date)
  • reasonstring

    Valores:"HIRING""RAISE""PROMOTION""ADJUSTMENT""ROLE_CHANGE""BENEFITS_REVIEW"

  • baseSalaryMonthlyCentsinteger
  • salaryPaidMonthsnumber
  • seniorityBonusCentsinteger
  • coverflexMonthlyCentsinteger | nullpode ser null
  • expenseAllowanceCentsinteger | nullpode ser null
  • workingTimeExemptionobject

    Isenção de horário de trabalho (CT arts. 218.º e 219.º).

    Campos
    • enabledbooleanobrigatório
    • modalitystring | nullpode ser null

      Valores:"NOT_SUBJECT_TO_PNT_LIMITS""EXTENDED_DAILY_OR_WEEKLY""PNT_OBSERVED"

    • compensationModestring | nullpode ser null

      Valores:"PERCENT_OF_BASE""HOURS_PER_DAY""HOURS_PER_WEEK""FIXED_MONTHLY"

    • compensationValuenumber | nullpode ser null

      Percentagem do vencimento base, horas ou euros por mês, conforme o modo de cálculo.

    • agreementDatestring (date) | nullpode ser null
  • externalobject

    Tarifa de um prestador de serviços. É remuneração: pede também o âmbito compensation:write e só dono e RH.

    Campos
    • rateTypestringobrigatório

      Valores:"HOURLY""DAILY""MONTHLY"

    • rateAmountCentsintegerobrigatório
    • unitsPerMonthnumber | nullpode ser null
    • vacationPaidboolean | nullpode ser null
    • absencesPaidboolean | nullpode ser null
    • monthsPaidPerYearinteger | nullpode ser null
  • notesstring | nullpode ser null
  • justificationstringobrigatório

    Motivo da alteração, obrigatório. Fica no registo de auditoria com os valores antes e depois.

Respostas

  • 200OK
    • dataCompensationobrigatório
      Campos de Compensation
      • idstring (uuid)obrigatório
      • employeeIdstring (uuid)obrigatório
      • effectiveFromstring (date)obrigatório
      • effectiveTostring (date) | nullobrigatóriopode ser null
      • reasonstringobrigatório

        Valores:"HIRING""RAISE""PROMOTION""ADJUSTMENT""ROLE_CHANGE""BENEFITS_REVIEW"

      • currencystringobrigatório

        Valores:"EUR"

      • baseSalaryMonthlyCentsintegerobrigatório
      • salaryPaidMonthsintegerobrigatório
      • mealAllowanceDailyCentsintegerobrigatório
      • mealAllowanceFormstringobrigatório
      • seniorityBonusCentsintegerobrigatório
      • coverflexMonthlyCentsinteger | nullobrigatóriopode ser null
      • expenseAllowanceCentsinteger | nullobrigatóriopode ser null
      • workingTimeExemptionobjectobrigatório

        Isenção de horário de trabalho (CT arts. 218.º e 219.º).

        Campos
        • enabledbooleanobrigatório
        • modalitystring | nullobrigatóriopode ser null

          Valores:"NOT_SUBJECT_TO_PNT_LIMITS""EXTENDED_DAILY_OR_WEEKLY""PNT_OBSERVED"

        • compensationModestring | nullobrigatóriopode ser null

          Valores:"PERCENT_OF_BASE""HOURS_PER_DAY""HOURS_PER_WEEK""FIXED_MONTHLY"

        • compensationValuenumber | nullobrigatóriopode ser null

          Percentagem do vencimento base, horas ou euros por mês, conforme o modo de cálculo.

        • agreementDatestring (date) | nullobrigatóriopode ser null
      • externalobject | nullobrigatóriopode ser null
        Campos
        • rateTypestring | nullobrigatóriopode ser null
        • rateAmountCentsinteger | nullobrigatóriopode ser null
        • unitsPerMonthnumber | nullobrigatóriopode ser null
        • vacationPaidboolean | nullobrigatóriopode ser null
        • absencesPaidboolean | nullobrigatóriopode ser null
        • monthsPaidPerYearinteger | nullobrigatóriopode ser null
      • notesstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 428Falta o If-Match ou a Idempotency-Key, obrigatórios nesta escrita
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/employees/{id}/compensation/{entryId}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/compensation/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -H 'If-Match: W/"…"' \
  -d '{
  "effectiveFrom": "2026-11-01",
  "justification": "Revisão salarial anual aprovada pela administração"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "effectiveFrom": "2026-10-16",
    "effectiveTo": "2026-10-16",
    "reason": "HIRING",
    "currency": "EUR",
    "baseSalaryMonthlyCents": 0,
    "salaryPaidMonths": 0,
    "mealAllowanceDailyCents": 0,
    "mealAllowanceForm": "string",
    "seniorityBonusCents": 0,
    "coverflexMonthlyCents": 0,
    "expenseAllowanceCents": 0,
    "workingTimeExemption": {
      "enabled": true,
      "modality": "NOT_SUBJECT_TO_PNT_LIMITS",
      "compensationMode": "PERCENT_OF_BASE",
      "compensationValue": 0,
      "agreementDate": "2026-10-16"
    },
    "external": {
      "rateType": "string",
      "rateAmountCents": 0,
      "unitsPerMonth": 0,
      "vacationPaid": true,
      "absencesPaid": true,
      "monthsPaidPerYear": 0
    },
    "notes": "string",
    "createdAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Registar ou pedir a mudança do IBAN

PUT/v1/employees/{id}/bank-details

Pode o próprio, o dono ou o RH. Sem IBAN na ficha (onboarding), o primeiro grava logo (APPLIED). A mudança de um IBAN existente nunca muda a ficha: cria um pedido pendente, cifrado, que só tem efeito quando o colaborador o confirma na app com um código (PENDING_CONFIRMATION); substitui um pedido anterior por confirmar. Para uma mudança: sem conta na app, 409; sem a cifra configurada, 503. A resposta mostra só os últimos 4 dígitos.

Âmbito exigidobank-details:writesensível

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • ibanstringobrigatório

    IBAN português (PT50...). Se a pessoa ainda não tem IBAN, grava logo; se já tem, a mudança fica pendente até o colaborador a confirmar na app.

  • swiftBicstring | nullpode ser null

Respostas

  • 200OK
    • dataBankDetailsUpdateobrigatório
      Campos de BankDetailsUpdate
      • employeeIdstring (uuid)obrigatório
      • outcomestringobrigatório

        APPLIED: era o primeiro IBAN e ficou gravado. PENDING_CONFIRMATION: mudança de um IBAN existente, à espera do colaborador na app.

        Valores:"APPLIED""PENDING_CONFIRMATION"

      • ibanMaskedstring | nullobrigatóriopode ser null

        O IBAN em vigor na ficha, mascarado (só os últimos 4 dígitos).

      • pendingPendingBankDetailsChange | nullobrigatóriopode ser null
        Campos de PendingBankDetailsChange
        • idstring (uuid)obrigatório
        • employeeIdstring (uuid)obrigatório
        • statusstringobrigatório

          PENDING até o colaborador decidir. CONFIRMED aplica o IBAN; REJECTED, CANCELLED e EXPIRED deixam o anterior.

          Valores:"PENDING""CONFIRMED""REJECTED""EXPIRED""CANCELLED"

        • ibanMaskedstringobrigatório

          Mascarado: só os últimos 4 dígitos.

        • requestedAtstring (date-time)obrigatório
        • expiresAtstring (date-time)obrigatório

          Até quando o colaborador pode confirmar. Nunca entra em vigor sozinha.

        • decidedAtstring (date-time) | nullobrigatóriopode ser null
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado
  • 503Serviço indisponível (por exemplo, sem a chave de cifra configurada)

Todos os erros seguem o formato de erro da API.

PUThttps://api.selfcare.work/v1/employees/{id}/bank-details
Pedido
curl -X PUT "https://api.selfcare.work/v1/employees/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/bank-details" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "iban": "PT50000201231234567890154"
}'
Resposta

OK

{
  "data": {
    "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "outcome": "APPLIED",
    "ibanMasked": "****0154",
    "pending": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "status": "PENDING",
      "ibanMasked": "****0154",
      "requestedAt": "2026-10-16T09:30:00.000Z",
      "expiresAt": "2026-10-16T09:30:00.000Z",
      "decidedAt": "2026-10-16T09:30:00.000Z"
    }
  },
  "error": null
}

Referência

Equipas

Equipas

GET/v1/teams
Âmbito exigidoteams:read

Parâmetros da query

  • updatedSince
    string (date-time)

    Exemplo: 2026-10-01T00:00:00Z

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<Team>obrigatório
      Campos de Team
      • idstring (uuid)obrigatório
      • namestringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • colorstringobrigatório
      • leaderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • membersarray<object>obrigatório
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
        • jobTitlestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/teams
Pedido
curl "https://api.selfcare.work/v1/teams?updatedSince=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "name": "string",
      "description": "string",
      "color": "string",
      "leader": {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string"
      },
      "members": [
        {
          "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
          "fullName": "string",
          "employeeNumber": "string",
          "jobTitle": "string"
        }
      ],
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Criar uma equipa

POST/v1/teams

Dono, RH ou gestor. Uma equipa tem sempre líder, que não tem de ser membro; o líder aprova as ausências de quem está na equipa.

Âmbito exigidoteams:write

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • namestringobrigatório
  • descriptionstring | nullpode ser null
  • leaderIdstring (uuid)obrigatório
  • colorstring

    Valores:"slate""blue""green""amber""rose""violet""teal""orange"

Respostas

  • 201Criado
    • dataTeamobrigatório
      Campos de Team
      • idstring (uuid)obrigatório
      • namestringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • colorstringobrigatório
      • leaderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • membersarray<object>obrigatório
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
        • jobTitlestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/teams
Pedido
curl -X POST "https://api.selfcare.work/v1/teams" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "name": "string",
  "leaderId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "name": "string",
    "description": "string",
    "color": "string",
    "leader": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "members": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string",
        "jobTitle": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Uma equipa

GET/v1/teams/{id}
Âmbito exigidoteams:read

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataTeamobrigatório
      Campos de Team
      • idstring (uuid)obrigatório
      • namestringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • colorstringobrigatório
      • leaderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • membersarray<object>obrigatório
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
        • jobTitlestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/teams/{id}
Pedido
curl "https://api.selfcare.work/v1/teams/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "name": "string",
    "description": "string",
    "color": "string",
    "leader": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "members": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string",
        "jobTitle": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Editar uma equipa

PATCH/v1/teams/{id}

Nome, descrição, cor ou líder. Mudar o líder muda as cadeias de aprovação. If-Match opcional.

Âmbito exigidoteams:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • namestring
  • descriptionstring | nullpode ser null
  • leaderIdstring (uuid)
  • colorstring

    Valores:"slate""blue""green""amber""rose""violet""teal""orange"

Respostas

  • 200OK
    • dataTeamobrigatório
      Campos de Team
      • idstring (uuid)obrigatório
      • namestringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • colorstringobrigatório
      • leaderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • membersarray<object>obrigatório
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
        • jobTitlestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/teams/{id}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/teams/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "name": "string"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "name": "string",
    "description": "string",
    "color": "string",
    "leader": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "members": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string",
        "jobTitle": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Apagar uma equipa

DELETE/v1/teams/{id}

Como na app: com documentos no espaço da equipa, 409. As pessoas ficam, só deixam de pertencer à equipa.

Âmbito exigidoteams:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Respostas

  • 200OK
    • dataTeamDeletedobrigatório
      Campos de TeamDeleted
      • idstring (uuid)obrigatório
      • deletedbooleanobrigatório

        Valores:true

    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

DELETEhttps://api.selfcare.work/v1/teams/{id}
Pedido
curl -X DELETE "https://api.selfcare.work/v1/teams/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "deleted": true
  },
  "error": null
}

Juntar uma pessoa a uma equipa

POST/v1/teams/{id}/members

Uma pessoa pode estar em várias equipas. Se já for membro, fica igual. Devolve a equipa.

Âmbito exigidoteams:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • employeeIdstring (uuid)obrigatório

Respostas

  • 200OK
    • dataTeamobrigatório
      Campos de Team
      • idstring (uuid)obrigatório
      • namestringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • colorstringobrigatório
      • leaderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • membersarray<object>obrigatório
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
        • jobTitlestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/teams/{id}/members
Pedido
curl -X POST "https://api.selfcare.work/v1/teams/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/members" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "employeeId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "name": "string",
    "description": "string",
    "color": "string",
    "leader": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "members": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string",
        "jobTitle": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Tirar uma pessoa de uma equipa

DELETE/v1/teams/{id}/members/{employeeId}

Quem não é membro dá 404. Devolve a equipa.

Âmbito exigidoteams:write

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)
  • employeeId
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Respostas

  • 200OK
    • dataTeamobrigatório
      Campos de Team
      • idstring (uuid)obrigatório
      • namestringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • colorstringobrigatório
      • leaderobject | nullobrigatóriopode ser null
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
      • membersarray<object>obrigatório
        Campos
        • idstring (uuid)obrigatório
        • fullNamestringobrigatório
        • employeeNumberstringobrigatório
        • jobTitlestring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

DELETEhttps://api.selfcare.work/v1/teams/{id}/members/{employeeId}
Pedido
curl -X DELETE "https://api.selfcare.work/v1/teams/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/members/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "name": "string",
    "description": "string",
    "color": "string",
    "leader": {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "fullName": "string",
      "employeeNumber": "string"
    },
    "members": [
      {
        "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
        "fullName": "string",
        "employeeNumber": "string",
        "jobTitle": "string"
      }
    ],
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Referência

Endpoints de webhooks

Avisos assinados quando algo muda (ausências, despesas, documentos, pessoas, equipas). Cada endpoint é de uma pessoa e só recebe o que ela vê no momento da entrega.

Listar os meus endpoints de webhooks

GET/v1/webhook-endpoints

Só os de quem pede, nesta empresa. O segredo nunca sai aqui.

Âmbito exigidowebhooks:manage

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<WebhookEndpoint>obrigatório
      Campos de WebhookEndpoint
      • idstring (uuid)obrigatório
      • urlstringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • eventsarray<string>obrigatório

        Valores:"absence.created""absence.updated""absence.approved""absence.rejected""absence.cancellation_requested""absence.cancelled""expense.submitted""expense.approved""expense.rejected""expense.paid""expense.cancelled""business_trip.submitted""business_trip.approved""business_trip.rejected""business_trip.cancelled""document.created""document.version_created""document.deleted""document.acknowledged""document.expiring""document.expired""document.review_requested""document.reviewed""employee.created""employee.updated""employee.status_changed""employee.contract_changed""employee.compensation_changed""employee.bank_details_change_requested""employee.bank_details_changed""employee.bank_details_change_rejected""team.created""team.updated""team.deleted""team.member_added""team.member_removed"

      • statusstringobrigatório

        Valores:"ACTIVE""DISABLED"

      • failingSincestring (date-time) | nullobrigatóriopode ser null

        Desde quando as entregas falham sem interrupção. Ao fim de 3 dias o endpoint desliga-se e o dono é avisado.

      • disabledAtstring (date-time) | nullobrigatóriopode ser null
      • disabledReasonstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/webhook-endpoints
Pedido
curl "https://api.selfcare.work/v1/webhook-endpoints" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "url": "https://erp.example.pt/selfcare/webhooks",
      "description": "string",
      "events": [
        "absence.created"
      ],
      "status": "ACTIVE",
      "failingSince": "2026-10-16T09:30:00.000Z",
      "disabledAt": "2026-10-16T09:30:00.000Z",
      "disabledReason": "string",
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Criar um endpoint de webhooks

POST/v1/webhook-endpoints

Cada entrega é um POST JSON assinado com HMAC-SHA256: Selfcare-Signature: t=<segundos>,v1=<hex de HMAC(segredo, ${t}.${corpo})>. Recusa assinaturas com mais de 5 minutos e deduplica pelo id do evento (entrega pelo menos uma vez, sem ordem garantida). Até 8 tentativas em cerca de 24 horas. O evento só leva ids, estado, datas e a empresa; o resto lê-se pela API. O segredo só aparece nesta resposta. Sem cifra configurada, 503.

Âmbito exigidowebhooks:manage

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Corpo do pedido

application/json
  • urlstring (uri)obrigatório

    Só https, porta 443, endereço público (sem redes privadas, loopback nem metadados). Os redireccionamentos não se seguem.

  • eventsarray<string>obrigatório

    Os tipos de evento a receber. O webhook.test chega sempre que se pede um teste.

    Valores:"absence.created""absence.updated""absence.approved""absence.rejected""absence.cancellation_requested""absence.cancelled""expense.submitted""expense.approved""expense.rejected""expense.paid""expense.cancelled""business_trip.submitted""business_trip.approved""business_trip.rejected""business_trip.cancelled""document.created""document.version_created""document.deleted""document.acknowledged""document.expiring""document.expired""document.review_requested""document.reviewed""employee.created""employee.updated""employee.status_changed""employee.contract_changed""employee.compensation_changed""employee.bank_details_change_requested""employee.bank_details_changed""employee.bank_details_change_rejected""team.created""team.updated""team.deleted""team.member_added""team.member_removed"

  • descriptionstring | nullpode ser null

Respostas

  • 201Criado
    • dataWebhookEndpointWithSecretobrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado
  • 503Serviço indisponível (por exemplo, sem a chave de cifra configurada)

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/webhook-endpoints
Pedido
curl -X POST "https://api.selfcare.work/v1/webhook-endpoints" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "url": "https://example.com",
  "events": [
    "absence.created"
  ]
}'
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "url": "https://erp.example.pt/selfcare/webhooks",
    "description": "string",
    "events": [
      "absence.created"
    ],
    "status": "ACTIVE",
    "failingSince": "2026-10-16T09:30:00.000Z",
    "disabledAt": "2026-10-16T09:30:00.000Z",
    "disabledReason": "string",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z",
    "secret": "whsec_…"
  },
  "error": null
}

Ver um endpoint de webhooks

GET/v1/webhook-endpoints/{id}
Âmbito exigidowebhooks:manage

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Respostas

  • 200OK
    • dataWebhookEndpointobrigatório
      Campos de WebhookEndpoint
      • idstring (uuid)obrigatório
      • urlstringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • eventsarray<string>obrigatório

        Valores:"absence.created""absence.updated""absence.approved""absence.rejected""absence.cancellation_requested""absence.cancelled""expense.submitted""expense.approved""expense.rejected""expense.paid""expense.cancelled""business_trip.submitted""business_trip.approved""business_trip.rejected""business_trip.cancelled""document.created""document.version_created""document.deleted""document.acknowledged""document.expiring""document.expired""document.review_requested""document.reviewed""employee.created""employee.updated""employee.status_changed""employee.contract_changed""employee.compensation_changed""employee.bank_details_change_requested""employee.bank_details_changed""employee.bank_details_change_rejected""team.created""team.updated""team.deleted""team.member_added""team.member_removed"

      • statusstringobrigatório

        Valores:"ACTIVE""DISABLED"

      • failingSincestring (date-time) | nullobrigatóriopode ser null

        Desde quando as entregas falham sem interrupção. Ao fim de 3 dias o endpoint desliga-se e o dono é avisado.

      • disabledAtstring (date-time) | nullobrigatóriopode ser null
      • disabledReasonstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/webhook-endpoints/{id}
Pedido
curl "https://api.selfcare.work/v1/webhook-endpoints/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "url": "https://erp.example.pt/selfcare/webhooks",
    "description": "string",
    "events": [
      "absence.created"
    ],
    "status": "ACTIVE",
    "failingSince": "2026-10-16T09:30:00.000Z",
    "disabledAt": "2026-10-16T09:30:00.000Z",
    "disabledReason": "string",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Alterar um endpoint de webhooks

PATCH/v1/webhook-endpoints/{id}

URL, eventos, descrição, ou ligar e desligar. If-Match opcional.

Âmbito exigidowebhooks:manage

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Corpo do pedido

application/json
  • urlstring (uri)

    Só https, porta 443, endereço público (sem redes privadas, loopback nem metadados). Os redireccionamentos não se seguem.

  • eventsarray<string>

    Os tipos de evento a receber. O webhook.test chega sempre que se pede um teste.

    Valores:"absence.created""absence.updated""absence.approved""absence.rejected""absence.cancellation_requested""absence.cancelled""expense.submitted""expense.approved""expense.rejected""expense.paid""expense.cancelled""business_trip.submitted""business_trip.approved""business_trip.rejected""business_trip.cancelled""document.created""document.version_created""document.deleted""document.acknowledged""document.expiring""document.expired""document.review_requested""document.reviewed""employee.created""employee.updated""employee.status_changed""employee.contract_changed""employee.compensation_changed""employee.bank_details_change_requested""employee.bank_details_changed""employee.bank_details_change_rejected""team.created""team.updated""team.deleted""team.member_added""team.member_removed"

  • descriptionstring | nullpode ser null
  • enabledboolean

    false desliga; true volta a ligar e recomeça a contagem das falhas.

Respostas

  • 200OK
    • dataWebhookEndpointobrigatório
      Campos de WebhookEndpoint
      • idstring (uuid)obrigatório
      • urlstringobrigatório
      • descriptionstring | nullobrigatóriopode ser null
      • eventsarray<string>obrigatório

        Valores:"absence.created""absence.updated""absence.approved""absence.rejected""absence.cancellation_requested""absence.cancelled""expense.submitted""expense.approved""expense.rejected""expense.paid""expense.cancelled""business_trip.submitted""business_trip.approved""business_trip.rejected""business_trip.cancelled""document.created""document.version_created""document.deleted""document.acknowledged""document.expiring""document.expired""document.review_requested""document.reviewed""employee.created""employee.updated""employee.status_changed""employee.contract_changed""employee.compensation_changed""employee.bank_details_change_requested""employee.bank_details_changed""employee.bank_details_change_rejected""team.created""team.updated""team.deleted""team.member_added""team.member_removed"

      • statusstringobrigatório

        Valores:"ACTIVE""DISABLED"

      • failingSincestring (date-time) | nullobrigatóriopode ser null

        Desde quando as entregas falham sem interrupção. Ao fim de 3 dias o endpoint desliga-se e o dono é avisado.

      • disabledAtstring (date-time) | nullobrigatóriopode ser null
      • disabledReasonstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

PATCHhttps://api.selfcare.work/v1/webhook-endpoints/{id}
Pedido
curl -X PATCH "https://api.selfcare.work/v1/webhook-endpoints/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11" \
  -d '{
  "url": "https://example.com"
}'
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "url": "https://erp.example.pt/selfcare/webhooks",
    "description": "string",
    "events": [
      "absence.created"
    ],
    "status": "ACTIVE",
    "failingSince": "2026-10-16T09:30:00.000Z",
    "disabledAt": "2026-10-16T09:30:00.000Z",
    "disabledReason": "string",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Apagar um endpoint de webhooks

DELETE/v1/webhook-endpoints/{id}

Apaga também o registo de entregas dele.

Âmbito exigidowebhooks:manage

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

  • If-Match
    string

    O ETag lido; se o recurso mudou entretanto, 412.

Respostas

  • 200OK
    • dataWebhookEndpointDeletedobrigatório
      Campos de WebhookEndpointDeleted
      • idstring (uuid)obrigatório
      • deletedbooleanobrigatório

        Valores:true

    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 412O recurso mudou (If-Match)
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

DELETEhttps://api.selfcare.work/v1/webhook-endpoints/{id}
Pedido
curl -X DELETE "https://api.selfcare.work/v1/webhook-endpoints/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "deleted": true
  },
  "error": null
}

Rodar o segredo

POST/v1/webhook-endpoints/{id}/rotate-secret

Devolve o segredo novo, uma vez. Durante 24 horas cada entrega leva duas assinaturas v1 (a nova e a anterior), para trocares sem perder nada.

Âmbito exigidowebhooks:manage

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 200OK
    • dataWebhookEndpointWithSecretobrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado
  • 503Serviço indisponível (por exemplo, sem a chave de cifra configurada)

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/webhook-endpoints/{id}/rotate-secret
Pedido
curl -X POST "https://api.selfcare.work/v1/webhook-endpoints/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/rotate-secret" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

OK

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "url": "https://erp.example.pt/selfcare/webhooks",
    "description": "string",
    "events": [
      "absence.created"
    ],
    "status": "ACTIVE",
    "failingSince": "2026-10-16T09:30:00.000Z",
    "disabledAt": "2026-10-16T09:30:00.000Z",
    "disabledReason": "string",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z",
    "secret": "whsec_…"
  },
  "error": null
}

Enviar um evento de teste

POST/v1/webhook-endpoints/{id}/test

Manda um webhook.test só a este endpoint (tem de estar ligado; senão 409) e devolve a entrega.

Âmbito exigidowebhooks:manage

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 201Criado
    • dataWebhookDeliveryobrigatório
      Campos de WebhookDelivery
      • idstring (uuid)obrigatório
      • endpointIdstring (uuid)obrigatório
      • eventIdstring (uuid)obrigatório
      • eventTypestringobrigatório

        Valores:"absence.created""absence.updated""absence.approved""absence.rejected""absence.cancellation_requested""absence.cancelled""expense.submitted""expense.approved""expense.rejected""expense.paid""expense.cancelled""business_trip.submitted""business_trip.approved""business_trip.rejected""business_trip.cancelled""document.created""document.version_created""document.deleted""document.acknowledged""document.expiring""document.expired""document.review_requested""document.reviewed""employee.created""employee.updated""employee.status_changed""employee.contract_changed""employee.compensation_changed""employee.bank_details_change_requested""employee.bank_details_changed""employee.bank_details_change_rejected""team.created""team.updated""team.deleted""team.member_added""team.member_removed""webhook.test"

      • statusstringobrigatório

        PENDING (a tentar), SUCCEEDED (2xx), FAILED (8 tentativas sem 2xx) ou SKIPPED (o dono já não vê o recurso, perdeu o acesso ou o endpoint foi desligado).

        Valores:"PENDING""SUCCEEDED""FAILED""SKIPPED"

      • attemptsintegerobrigatório
      • lastAttemptAtstring (date-time) | nullobrigatóriopode ser null
      • nextAttemptAtstring (date-time) | nullobrigatóriopode ser null
      • responseStatusinteger | nullobrigatóriopode ser null
      • responseBodyExcerptstring | nullobrigatóriopode ser null

        Os primeiros 2 KB da resposta do teu servidor.

      • durationMsinteger | nullobrigatóriopode ser null
      • errorstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/webhook-endpoints/{id}/test
Pedido
curl -X POST "https://api.selfcare.work/v1/webhook-endpoints/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/test" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "endpointId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "eventId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "eventType": "absence.created",
    "status": "PENDING",
    "attempts": 0,
    "lastAttemptAt": "2026-10-16T09:30:00.000Z",
    "nextAttemptAt": "2026-10-16T09:30:00.000Z",
    "responseStatus": 0,
    "responseBodyExcerpt": "string",
    "durationMs": 0,
    "error": "string",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Registo de entregas

GET/v1/webhook-endpoints/{id}/deliveries

Os últimos 30 dias: estado, tentativas, código e primeiros 2 KB da resposta, duração.

Âmbito exigidowebhooks:manage

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Parâmetros da query

  • limit
    integer

    Itens por página. Por omissão 50, máximo 200.

  • cursor
    string

    O meta.nextCursor da página anterior, com os mesmos filtros.

Respostas

  • 200OK
    • dataarray<WebhookDelivery>obrigatório
      Campos de WebhookDelivery
      • idstring (uuid)obrigatório
      • endpointIdstring (uuid)obrigatório
      • eventIdstring (uuid)obrigatório
      • eventTypestringobrigatório

        Valores:"absence.created""absence.updated""absence.approved""absence.rejected""absence.cancellation_requested""absence.cancelled""expense.submitted""expense.approved""expense.rejected""expense.paid""expense.cancelled""business_trip.submitted""business_trip.approved""business_trip.rejected""business_trip.cancelled""document.created""document.version_created""document.deleted""document.acknowledged""document.expiring""document.expired""document.review_requested""document.reviewed""employee.created""employee.updated""employee.status_changed""employee.contract_changed""employee.compensation_changed""employee.bank_details_change_requested""employee.bank_details_changed""employee.bank_details_change_rejected""team.created""team.updated""team.deleted""team.member_added""team.member_removed""webhook.test"

      • statusstringobrigatório

        PENDING (a tentar), SUCCEEDED (2xx), FAILED (8 tentativas sem 2xx) ou SKIPPED (o dono já não vê o recurso, perdeu o acesso ou o endpoint foi desligado).

        Valores:"PENDING""SUCCEEDED""FAILED""SKIPPED"

      • attemptsintegerobrigatório
      • lastAttemptAtstring (date-time) | nullobrigatóriopode ser null
      • nextAttemptAtstring (date-time) | nullobrigatóriopode ser null
      • responseStatusinteger | nullobrigatóriopode ser null
      • responseBodyExcerptstring | nullobrigatóriopode ser null

        Os primeiros 2 KB da resposta do teu servidor.

      • durationMsinteger | nullobrigatóriopode ser null
      • errorstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
    • metaobjectobrigatório
      Campos
      • nextCursorstring | nullobrigatóriopode ser null
      • hasMorebooleanobrigatório
  • 400Pedido mal formado ou cursor inválido
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

GEThttps://api.selfcare.work/v1/webhook-endpoints/{id}/deliveries
Pedido
curl "https://api.selfcare.work/v1/webhook-endpoints/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/deliveries" \
  -H "Authorization: Bearer sc_live_…"
Resposta

OK

{
  "data": [
    {
      "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "endpointId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "eventId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
      "eventType": "absence.created",
      "status": "PENDING",
      "attempts": 0,
      "lastAttemptAt": "2026-10-16T09:30:00.000Z",
      "nextAttemptAt": "2026-10-16T09:30:00.000Z",
      "responseStatus": 0,
      "responseBodyExcerpt": "string",
      "durationMs": 0,
      "error": "string",
      "createdAt": "2026-10-16T09:30:00.000Z",
      "updatedAt": "2026-10-16T09:30:00.000Z"
    }
  ],
  "error": null,
  "meta": {
    "nextCursor": "string",
    "hasMore": true
  }
}

Reenviar uma entrega

POST/v1/webhook-deliveries/{id}/redeliver

Cria uma entrega nova do mesmo evento (o mesmo id: deduplica). A visibilidade volta a ser verificada.

Âmbito exigidowebhooks:manage

Parâmetros do caminho

  • id
    obrigatório
    string (uuid)

Cabeçalhos

  • Idempotency-Key
    string

    Até 255 caracteres. Repetir com o mesmo corpo devolve a mesma resposta durante 24 horas.

Respostas

  • 201Criado
    • dataWebhookDeliveryobrigatório
      Campos de WebhookDelivery
      • idstring (uuid)obrigatório
      • endpointIdstring (uuid)obrigatório
      • eventIdstring (uuid)obrigatório
      • eventTypestringobrigatório

        Valores:"absence.created""absence.updated""absence.approved""absence.rejected""absence.cancellation_requested""absence.cancelled""expense.submitted""expense.approved""expense.rejected""expense.paid""expense.cancelled""business_trip.submitted""business_trip.approved""business_trip.rejected""business_trip.cancelled""document.created""document.version_created""document.deleted""document.acknowledged""document.expiring""document.expired""document.review_requested""document.reviewed""employee.created""employee.updated""employee.status_changed""employee.contract_changed""employee.compensation_changed""employee.bank_details_change_requested""employee.bank_details_changed""employee.bank_details_change_rejected""team.created""team.updated""team.deleted""team.member_added""team.member_removed""webhook.test"

      • statusstringobrigatório

        PENDING (a tentar), SUCCEEDED (2xx), FAILED (8 tentativas sem 2xx) ou SKIPPED (o dono já não vê o recurso, perdeu o acesso ou o endpoint foi desligado).

        Valores:"PENDING""SUCCEEDED""FAILED""SKIPPED"

      • attemptsintegerobrigatório
      • lastAttemptAtstring (date-time) | nullobrigatóriopode ser null
      • nextAttemptAtstring (date-time) | nullobrigatóriopode ser null
      • responseStatusinteger | nullobrigatóriopode ser null
      • responseBodyExcerptstring | nullobrigatóriopode ser null

        Os primeiros 2 KB da resposta do teu servidor.

      • durationMsinteger | nullobrigatóriopode ser null
      • errorstring | nullobrigatóriopode ser null
      • createdAtstring (date-time)obrigatório
      • updatedAtstring (date-time)obrigatório
    • errorany | nullobrigatóriopode ser null
  • 401Sem credencial válida
  • 402Conta da empresa bloqueada por falta de pagamento
  • 403Sem âmbito, sem permissão ou API bloqueada na empresa
  • 404Não existe ou o utilizador não o pode ver
  • 409Estado que não permite a operação
  • 422Campos inválidos
  • 429Demasiados pedidos (ver Retry-After)
  • 500Erro inesperado

Todos os erros seguem o formato de erro da API.

POSThttps://api.selfcare.work/v1/webhook-deliveries/{id}/redeliver
Pedido
curl -X POST "https://api.selfcare.work/v1/webhook-deliveries/3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10/redeliver" \
  -H "Authorization: Bearer sc_live_…" \
  -H "Idempotency-Key: 2f6c1a52-1d7e-4b0c-9a51-0c3f0b5b8e11"
Resposta

Criado

{
  "data": {
    "id": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "endpointId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "eventId": "3f1c2b8e-5d0a-4c1e-9f8a-2b7d6e5c4a10",
    "eventType": "absence.created",
    "status": "PENDING",
    "attempts": 0,
    "lastAttemptAt": "2026-10-16T09:30:00.000Z",
    "nextAttemptAt": "2026-10-16T09:30:00.000Z",
    "responseStatus": 0,
    "responseBodyExcerpt": "string",
    "durationMs": 0,
    "error": "string",
    "createdAt": "2026-10-16T09:30:00.000Z",
    "updatedAt": "2026-10-16T09:30:00.000Z"
  },
  "error": null
}

Perguntas frequentes

A API está incluída no meu plano?
Sim, em todos os planos, sem custo extra. Com a conta bloqueada por falta de pagamento, a API responde 402, como o resto da app.
Posso ler os salários ou os recibos de vencimento?
O processamento salarial fica fora da API e do MCP. Pela API REST, os recibos de vencimento já emitidos são documentos: cada pessoa vê os seus (e o RH os de quem gere), como na app. Pelo MCP, recibos, contratos e documentos de identificação ou de IBAN nunca saem. Contratos e remuneração (âmbitos sensíveis) só para quem os vê na app.
Porque é que recebo 404 num registo que existe?
Porque a pessoa da credencial não o vê na app. A API responde 404 em vez de 403 para não revelar que o registo existe.
Como vejo o IBAN de um colaborador?
Não vês: sai sempre mascarado (****0154). Podes registar o primeiro IBAN de uma ficha; alterar um existente fica pendente até o colaborador confirmar na app.
Uma chave serve para várias empresas?
Não. Cada chave é de uma pessoa numa empresa. Para várias empresas, cria uma chave em cada uma ou usa OAuth, em que a pessoa escolhe a empresa ao autorizar.
Onde ficam os dados?
Na União Europeia. A API é servida a partir do mesmo sistema da app, com o mesmo isolamento por empresa.
Como sei quem fez o quê pela API?
Cada escrita fica no registo de auditoria da empresa com a origem (API ou MCP), a chave ou a app e o id do pedido. Cada pessoa vê o registo de acessos das suas chaves em Definições → API → Chaves da API.
Os contabilistas podem usar a API?
Podem usar chaves com o que o papel deles permite na app. O servidor MCP não está disponível para contabilistas.
Encontrei um problema ou preciso de um endpoint novo.
Escreve para suporte@selfcare.work com o requestId da resposta.

Alterações

Mudanças na API v1. Acrescentar campos, endpoints ou eventos não muda de versão; remover ou mudar o significado de algo só numa v2, com aviso prévio.

  • 2026-10-19Guias completos no portal (Idempotency-Key, webhooks com verificação em código, MCP passo a passo, app móvel, perguntas frequentes). A especificação corrige os campos anuláveis que apontam para um esquema (Document.currentVersion, o IBAN pendente e o saldo do resumo passam a anyOf com null) e passa no redocly lint sem erros nem avisos.
  • 2026-10-19Servidor MCP em https://api.selfcare.work/mcp (Streamable HTTP), com registo dinâmico de clientes (/oauth/register, RFC 7591), metadados do recurso protegido (RFC 9728) e tokens presos ao recurso (RFC 8707). GET /v1/employees aceita q para procurar por nome.
  • 2026-10-19OAuth 2.1 para apps usadas por várias pessoas: authorization code com PKCE S256, /oauth/token, /oauth/revoke e /.well-known/oauth-authorization-server. App móvel: GET/POST /v1/me/devices, DELETE /v1/me/devices/{id}, notificações push e GET /v1/me/summary. SDK TypeScript gerado da especificação.
  • 2026-10-18Webhooks assinados (/v1/webhook-endpoints, entregas, reenvio, teste e rotação do segredo). Escritas: ausências, despesas, deslocações, documentos, colaboradores, equipas, contratos, remuneração e IBAN (a alteração fica pendente até o colaborador confirmar). Idempotency-Key em todas as escritas e If-Match nas edições.
  • 2026-10-17Primeira versão: chaves pessoais (sc_live_, sc_test_), âmbitos, limites, paginação por cursor, erros estáveis e leituras de ausências, aprovações, despesas, deslocações, documentos, colaboradores e equipas.
API do Selfcare