# Recruit API — LLM Context File
# Version: 2.3
# Updated: 2026-06-03
# Purpose: Structured reference for AI agents, LLMs, and automation tools.
# Full OpenAPI spec: https://encontreumnerd.com.br/docs/api/openapi.json
# Changelog v2.2: https://encontreumnerd.com.br/docs/api/changelog-v2.2 (also at docs/modules/OPENCLAW-API-CHANGELOG-v2.2.md)
# Plugin manifest: https://encontreumnerd.com.br/.well-known/ai-plugin.json
## Overview
Recruit API is the REST API for the Eu Nerd IT Services platform (Brazil's largest IT field service network, 9000+ technicians, 2900 cities). It provides programmatic access to 23 resources across talent management, CRM, ticketing, contracts, invoicing, content, email, and messaging. All endpoints require Bearer token authentication. No public/unauthenticated endpoints exist.
## Connection
- Base URL: https://wzcftlekhxlsavsjgfhb.supabase.co/functions/v1/openclaw-api
- Auth: Header `Authorization: Bearer {RECRUIT_API_KEY}`
- Format: JSON request/response
- Pagination: `?limit=50&offset=0` (max 500 per page)
- Search: `?search=text` (searches relevant fields per resource)
- Rate limit: 200 req/min per API key
- Volume limit: 5000 records/hour per API key (exfiltration protection)
- All timestamps: ISO 8601 UTC
## Security
- All endpoints require valid Bearer token (401 on missing/invalid)
- Rate limiting enforced per API key (429 on exceed)
- Volume/exfiltration monitoring via api_access_logs table
- Service uses server-side service role (bypasses RLS) — API key acts as the auth boundary
- No CORS restrictions (designed for server-to-server and agent access)
## Resources and Endpoints (28 resources, 60+ endpoints)
### candidates (Talent Pool)
- GET /candidates — List. Filters: etapa, cidade, search (nome, email, cidade). Returns: id, nome, email, telefone, cidade, estado, nivel_senioridade, etapa, status, cnpj, created_at, total_jobs_completed.
- GET /candidates/{id} — Detail. Extra fields: cpf, bairro, nivel_ingles, certificacoes, tech_profile, job_categories, total_jobs_all, service_radius_km, schedule_availability, disponibilidade_inicio.
- POST /candidates — Create. Required: nome, email, telefone, cidade, estado. Optional: bairro, cep, endereco_linha, curriculo_url, curriculo_mime_type, certificacoes[], nivel_senioridade, nivel_ingles, observacoes. Auto: etapa=triagem.
- PATCH /candidates/{id} — Update. Allowed fields: nome, email, telefone, cidade, estado, bairro, etapa (triagem|entrevista|proposta|contratado|arquivado), status (adequado|talvez|nao_adequado), certificacoes, nivel_senioridade, nivel_ingles, observacoes, cnpj, cpf, disponibilidade_inicio, service_radius_km.
### vacancies (Job Postings)
- GET /vacancies — List. Filters: status (open|closed|draft), search (title, client_name). Returns: id, title, client_name, location, status, work_mode, contract_type, job_area, created_at, published_at.
- GET /vacancies/{id} — Detail. All fields.
- POST /vacancies — Create. Required: title. Optional: client_name, description, location, work_mode, contract_type, job_area, requirements, salary_range_min, salary_range_max. Auto: status=draft.
- PATCH /vacancies/{id} — Update. Allowed: title, description, client_name, location, status, work_mode, contract_type, job_area, requirements, salary_range_min, salary_range_max, published_at.
### vacancy-applications
- GET /vacancy-applications/{vacancy_id} — List applications for a vacancy. Filters: status. Returns candidate data inline (nome, email, telefone, cidade, estado) + form_answers.
### vacancy-requests (Client Vacancy Requests)
- GET /vacancy-requests — List. Filters: status (pending|approved|rejected), client_name, search (client_name, job_title).
- GET /vacancy-requests/{id} — Detail. All fields.
- POST /vacancy-requests — Create. Required: client_name, client_user_id. Optional: job_title, job_description, request_type (default: outsourcing), work_mode, contract_type, positions_count (default: 1), location_city, location_state, start_date, end_date, request_data. Auto: status=pending.
- PATCH /vacancy-requests/{id} — Update. Allowed: status, admin_notes, reviewed_by, reviewed_at, converted_vacancy_id, job_title, job_description, work_mode, contract_type, positions_count. Auto: reviewed_at on status=approved.
### allocations (Technician Assignments)
- GET /allocations — List. Search: title, client_name. Returns: id, title, candidate_id, client_name, status, start_date, end_date, work_mode, billing_type, billing_value, project_name.
- GET /allocations/{id} — Detail. Includes candidate join: nome, email, cidade, estado.
### scheduling (Interview Links)
- GET /scheduling — List. Filters: candidate_id, vacancy_id. Returns scheduling_url for each link.
- POST /scheduling — Create. Required: candidate_id, created_by. Optional: vacancy_id. Auto: expires in 7 days, token-based URL generated.
### companies (B2B Clients)
- GET /companies — List. Search: name, cnpj. Returns: id, name, cnpj, phone, is_active, created_at.
- GET /companies/{id} — Detail. All fields.
- POST /companies — Create. Required: name. Optional: cnpj, email, phone, city, state, segment. Auto: is_active=true.
- PATCH /companies/{id} — Update. Allowed: name, cnpj, email, phone, city, state, segment, is_active, contract_type, monthly_value, status.
### deal-pipelines (CRM Pipelines)
- GET /deal-pipelines — List active pipelines with stage configurations. Returns: id, name, slug, stages, default_vibe_tone, is_active, created_at.
- GET /deal-pipelines/{id}/stages — [v2.2] Canonical stage catalog for one pipeline. Returns each stage with `key` (slugified), `label`, `position`, `is_terminal`, `aliases[]`, plus top-level `default_stage` (first non-terminal) and `canonical_stage_slugs[]`. Falls back to `deal_pipelines.stages` when catalog table empty (`source` field indicates origin). Use this BEFORE writing any `stage` value to avoid 422.
### deals (CRM Deals)
- GET /deals — List. Filters: pipeline_id, stage, search (title, contact_name, contact_email).
- GET /deals/lookup?external_id=|source_id=|gmail_thread_id=|contact_email=&pipeline_id= — [v2.3] Direct read without paginated scan (up to 25 hits). `gmail_thread_id` matches `metadata->>gmail_thread_id`. `matched_by` reflects the filter used.
- GET /deals/resolve?gmail_thread_id=|contact_email=&pipeline_id= — [v2.3] Resolve a single deal. Returns `status: "resolved"` (with `deal_id`, `confidence:"high"`) when exactly one match, `status: "ambiguous"` (with `candidates[]` + `required_action:"manual_canonicalization"`) when 2+ matches, or `status: "not_found"` when none. Route is matched BEFORE `/{id}` so it never collides with deal ids.
- GET /deals/enums/lost-reasons — [v2.3] Canonical list of accepted `lost_reason_code` values with PT-BR labels (includes legacy PT codes `cliente_escolheu_outro` etc. plus new English codes `lost_to_competitor`, `customer_discarded`, `timing_lost`, `no_active_demand`, `price_not_accepted`, `no_response_cycle_exhausted`).
- GET /deals/duplicates?contact_email=&min=2 — Guardrail: lists OPEN deals sharing the same email; `is_duplicate:true` when count ≥ min. Use before creating to avoid duplicates.
- GET /deals/{id} — Detail. Includes resolved company_name. [v2.2] Supports `?expand=company,activities_count,last_activity_at,stage_history_summary` (comma-separated) to bundle related data in one round-trip. Default payload unchanged when `expand` omitted.
- POST /deals — Create. Required: pipeline_id. Optional: company_name (auto-resolves company_id), contact_name, contact_email, contact_phone, description, stage (default: lead), source, external_id. Auto: title from contact/company, stage_entered_at, source=api. [v2.2] When `external_id` provided and a deal with same `metadata->>external_id` exists → 409 `external_id_conflict` (includes `existing_deals[]` + hint). Bypass with `?force=1` to create parallel deal (audited).
- POST /deals/canonical-resolution — [v2.2] Elect canonical (master) deal across duplicates. Body: `{ gmail_thread_id?, contact_email?, pipeline_id?, apply?: false, reason? }`. Default is dry-run; with `apply:true` marks duplicates by setting `master_deal_id` on non-canonical rows + writes `canonical_resolution` activities on both sides. At least one of `gmail_thread_id` or `contact_email` is required.
- PATCH /deals/{id} — Update. Allowed: stage, value, priority, contact_name, contact_email, contact_phone, notes, owner_id. Auto: stage_entered_at on stage change + deal_activity logged. [v2.2] When stage transition rejected, response now includes `accepted_values[]` (canonical labels) and `canonical_stage_slugs[]`; pass `?force=1` to bypass (audited). Opt-in strict mode: `?stage_strict=1` returns 422 `unknown_stage` (with `allowed_stages[]`) when post-normalization stage is not in the pipeline catalog. Successful PATCH returns header `X-Read-Your-Writes: true`; if stage was normalized, also returns `X-Stage-Normalized: =>`.
### deal-activities (CRM Activity Log)
- GET /deal-activities?deal_id={id} — List. Required: deal_id. Filters: type (note|stage_change|email|call|meeting|canonical_resolution).
- POST /deals/{id}/activities — Create. Required: type, content. Optional: metadata, source. [v2.2] Idempotent by `metadata.gmail_message_id`, `metadata.idempotency_key`, or header `Idempotency-Key`. Replays return 200 with header `X-Idempotent-Replay: true` (no duplicate row created).
### proposals (Commercial Proposals)
- GET /proposals — List. Filters: status (rascunho|ativa|aceita|expirada), client_name, proposal_type, search (title, client_name). Returns: id, title, slug, client_name, proposal_type, status, sent_at, accepted_at, expires_at, created_at.
- GET /proposals/{id} — Detail. Returns ALL fields including content, metadata, original_content, signing_token, client_logo_url, source_file_url, vacancy_request_id, created_by, accepted_by, updated_at.
- GET /proposals/{id}/tracking — Tracking data: signing_token (for public URL /proposta/{token}), sent_at, accepted_at, accepted_by, expires_at, metadata.
- POST /proposals — Create. Required: title, client_name. Optional: proposal_type (default: custom), recipient_name, recipient_email, content ({ sections: [{ title, html, order }] }), metadata (investment, scope, brief), expires_at, slug. Auto: slug generated from client_name if omitted, signing_token (UUID) generated, status=rascunho.
- PATCH /proposals/{id} — Update. Allowed: status, title, client_name, content, metadata, expires_at, recipient_name, recipient_email, proposal_type, client_logo_url, source_file_url. Auto: sent_at on status→ativa, accepted_at on status→aceita.
- Status lifecycle: rascunho → ativa (sends) → aceita (client accepts via /proposta/{signing_token}) or expirada (auto-expire cron).
- Content structure: { sections: [{ title: string, html: string, order: number }] }. Each section is a block of the proposal.
- Metadata can hold: investment details, scope fields, brief answers, and any custom key-value pairs for the proposal context.
### service-orders (Field Service)
- GET /service-orders — List. Search: code (OS-YYYY-XXXXX), company_name.
- GET /service-orders/{id} — Detail. All fields.
- POST /service-orders — Create. Required: company_name, description. Optional: category, priority (default: medium), scheduled_date, candidate_id, contact_name, contact_phone, city, state, address. Auto: code=OS-YYYY-XXXXX, status=pending, source=api.
- PATCH /service-orders/{id} — Update. Allowed: status, priority, category, candidate_id, scheduled_date, completion_notes, rating, description. Auto: completed_at on status=completed.
### tickets (Helpdesk)
- GET /tickets — List. Filters: status (open|in_progress|resolved|closed), search (ticket_number, subject, client_name).
- GET /tickets/{id} — Detail with all client_ticket_messages.
- POST /tickets — Create. Required: subject, sender_email. Optional: client_name, category, priority (default: medium), sender_name, source (default: api), parsed_data. Auto: ticket_number=TK-YYYYMMDD-XXXX, status=open.
- PATCH /tickets/{id} — Update. Allowed: status, priority, category, assigned_to, resolution_notes. Auto: resolved_at on status=resolved|closed, updated_at always.
### leads (CRM Leads)
- GET /leads — List. Filters: status, source (diagnostic-form|diagnostic-form-partial|contact-form|website|crm-deal), search (sender_email, sender_name, subject, client_name). Returns parsed_data with score, CNPJ, recommended modules.
- GET /leads/{id} — Detail with full parsed_data (score, maturityLevel, recommendations, wizardAnswers).
### contracts (Digital Contracts)
- GET /contracts — List. Returns: id, status, candidate_id, employee_id, template_id, sent_at, signed_at, created_at.
- GET /contracts/{id} — Detail with contract_signatures[].
### document-signatures (Document Signing)
- GET /document-signatures — List. Filters: status, search (title).
- GET /document-signatures/{id} — Detail with document_signature_recipients[].
- GET /document-signatures/{id}/status — Summary: total, signed, pending counts + per-recipient signing_urls for pending.
- GET /document-signatures/{id}/download — format=url (1h signed URL) or format=binary (PDF bytes). Prefers signed version.
- POST /document-signatures — Create. Required: title, file_url, file_name, recipients[{name, email}]. Optional: message, file_type (pdf|docx), created_by. External URLs downloaded + stored automatically. Signing emails sent.
- POST /document-signatures/{id}/resend — Resend. Optional: recipient_id (specific) or all pending.
- POST /document-signatures/{id}/cancel — Cancel request + pending recipients.
### invoice-batches (Payment Processing)
- GET /invoice-batches — List with stats (total_employees, total_invoice_value, counts by status). Filters: status, search (title).
- GET /invoice-batches/{id} — Detail with invoices (+ employee data), transactions, stats.
- GET /invoice-batches/{id}/status — Payment transaction summary: pending, processing, completed, failed, returned.
- POST /invoice-batches — [v2.2] Create batch. Required: title, reference_month (YYYY-MM-DD). Optional: deadline, notes, status (open|processing|closed|paid|cancelled, default open), created_by (UUID), created_by_email. `created_by` is resolved in this order: explicit UUID → employees.email lookup → auth.users admin lookup → first `master_admin` fallback. Returns 422 `created_by_unresolved` if no UUID can be derived. Returns 201 + created batch.
- POST /invoice-batches/{id}/process — Create Transfeera PIX/TED transfers. Features: anti-duplication (idempotency_key), auto-split values >R$15,000, PIX key type auto-detection (CPF/CNPJ/EMAIL/TELEFONE/CHAVE_ALEATORIA), bank account fallback. Invoices transition to processing.
- POST /invoice-batches/{id}/close — Close batch, trigger payments. IRREVERSIBLE. Must /process first.
### employees (Team Members)
- GET /employees — List. Filters: status, departamento, tipo_contrato, search (nome, email, cargo). Returns: id, nome, email, telefone, cpf, cnpj, cargo, departamento, status, tipo_contrato, data_admissao, data_desligamento, cidade, estado, salario, created_at.
- GET /employees/{id} — Detail. All fields including pix_key, banco, agencia, conta, observacoes, foto_url, candidate_id, user_id.
- POST /employees — Create. Required: nome, email. Optional: cargo, departamento, tipo_contrato (pj|clt|estagio), data_admissao, telefone, cpf, cnpj, cidade, estado, salario, pix_key, banco, agencia, conta, observacoes, candidate_id, user_id, created_by. Auto: cargo=Colaborador, departamento=Geral, tipo_contrato=pj.
### employee-invoices (Employee Payment)
- GET /employee-invoices — List. Filters: status, batch_id, employee_id.
- POST /employee-invoices — Create. Required: batch_id, employee_id. Optional: invoice_value, expected_value, description, notes, status (default: requested). Validates batch is open, employee exists, prevents duplicates (409).
- GET /employee-invoices/{id} — Detail with employee data (nome, email, cnpj, cargo, departamento).
- PATCH /employee-invoices/{id} — Update. Allowed: status, paid_at, notes, final_value, rejection_reason. Auto: paid_at on status=paid.
### invoice-adjustments (Extras & Deductions)
- GET /invoice-adjustments?invoice_id={id} — List adjustments for an invoice. Returns data + summary (total_additions, total_deductions, net_applied_to_invoice).
- POST /invoice-adjustments — Create. Required: invoice_id, title, type (addition|deduction), value. Optional: apply_to_invoice (default: true), notes. When apply_to_invoice=true, value affects fiscal total. When false, tracked only.
- DELETE /invoice-adjustments/{id} — Remove adjustment.
### provider-invoices (Provider Payment)
- GET /provider-invoices — List. Filters: status, candidate_id, search (code).
- GET /provider-invoices/{id} — Detail with candidate data (nome, email, cnpj).
- PATCH /provider-invoices/{id} — Update. Allowed: status, notes, rejection_reason, estimated_payment_date, paid_at. Auto: reviewed_at on approved, paid_at on paid.
### whatsapp (Messaging)
- POST /whatsapp — Send template message via Meta Cloud API. Required: template_id (UUID), phone. Optional: variables (key-value). Phone auto-normalized to E.164 (accepts 11999999999, 5511999999999, +5511999999999). Returns: wamid, provider_response.
### articles (Blog/Knowledge Base)
- GET /articles — List. Filters: status (draft|published|archived), category, search (title, slug).
- GET /articles/{id_or_slug} — Detail. Accepts UUID or slug. All fields including SEO.
- POST /articles — Create. Required: title. Optional: slug (auto-generated from title, NFD normalized), content_html, content_markdown, category, status (default: draft), excerpt, tags[], meta_title, meta_description, meta_keywords, featured_image_url, featured_image_alt, author_name, subtitle, reading_time_minutes, word_count, og_title, og_description, og_image, canonical_url, schema_markup, structured_data. Auto: published_at on status=published. 409 on duplicate slug.
- PATCH /articles/{id_or_slug} — Update. All POST fields updatable. Auto: published_at on transition to published.
- DELETE /articles/{id_or_slug} — Permanently delete.
### metrics (Platform Analytics)
- GET /metrics — Aggregated: candidate counts by stage (via get_talent_pool_metrics RPC), total_vacancies, open_tickets, active_service_orders.
### payables (Contas a Pagar)
- GET /payables — List. Filters: status, from, to, search. Returns: id, description, amount, due_date, paid_date, paid_amount, status, source, company_id, category_id, notes, created_at.
- GET /payables/{id} — Detail with joined category, company, bank_account.
- POST /payables — Create. Required: description, amount, due_date.
- PATCH /payables/{id} — Update. Allowed: description, amount, due_date, status, paid_date, paid_amount, notes, category_id, bank_account_id.
### receivables (Contas a Receber)
- GET /receivables — List. Filters: status, from, to, search. Fields: received_date, received_amount instead of paid_date, paid_amount.
- GET /receivables/{id} — Detail with joined category, company, bank_account.
- POST /receivables — Create. Required: description, amount, due_date.
- PATCH /receivables/{id} — Update. Allowed: description, amount, due_date, status, received_date, received_amount, notes, category_id, bank_account_id.
### cashflow (Fluxo de Caixa)
- GET /cashflow — Daily projection. Params: from (default today), to or days (default 90). Returns totals + daily breakdown with balance and balance_adjusted (applies default_rate 3% + variable_cost 5% to unpaid receivables).
### finance-settings
- GET /finance-settings — Current settings.
- PATCH /finance-settings — Update default_default_rate, default_variable_cost_pct, payout_batch_day, horizon_days.
### recurring-rules (Regras Recorrentes)
- GET /recurring-rules — List. Filters: type (payable|receivable), is_active, search.
- GET /recurring-rules/{id} — Detail.
- POST /recurring-rules — Create. Required: type, description, amount, recurrence, start_date.
- PATCH /recurring-rules/{id} — Update.
## Response Format
List: `{ "data": [...], "total": 342, "limit": 50, "offset": 0 }`
Detail: `{ "data": { ... } }`
Error: `{ "error": "Description" }`
## HTTP Status Codes
200 Success | 201 Created | 400 Bad request | 401 Unauthorized | 404 Not found | 405 Method not allowed | 409 Conflict (duplicate slug) | 429 Rate limit | 500 Server error
## Business Rules (Auto-Fill Summary)
- candidates: etapa=triagem on create
- vacancies: status=draft on create
- employees: cargo=Colaborador, departamento=Geral, tipo_contrato=pj on create
- service-orders: status=pending, source=api, code=OS-YYYY-XXXXX on create
- tickets: status=open, ticket_number=TK-YYYYMMDD-XXXX on create
- deals: stage_entered_at on stage change + deal_activity auto-logged
- proposals: sent_at on status=ativa
- payables/receivables: status=pending, source=manual on create
- recurring-rules: is_active=true, next_due_date=start_date on create
- cashflow: read-only projection from finance_settings rates
### emails (Transactional Email)
- POST /emails — Send email via Resend. Required: to (email), subject, html. Optional: text, cc (string[]), bcc (string[]), from_email (@eunerd.com only), entity_type, entity_id, deal_id. Auto: email logged to email_logs, deal_activity created if deal_id provided. Sender defaults to noreply@eunerd.com.
### categories (Finance Categories)
- GET /categories — List. Filters: type (payable|receivable), search. Returns: id, name, type, parent_id, omie_code, is_active.
- GET /categories/{id} — Detail.
- POST /categories — Create. Required: name, type (payable|receivable). Optional: parent_id, omie_code, is_active. Upserts by omie_code if provided.
- PATCH /categories/{id} — Update. Allowed: name, parent_id, omie_code, is_active.
### bank-accounts (Contas Bancárias)
- GET /bank-accounts — List. Filters: is_active, search. Returns: id, name, bank_name, agency, account_number, pix_key, is_active, current_balance, omie_account_id, created_at.
- GET /bank-accounts/{id} — Detail.
### interview-bookings (Agendamentos de Entrevista)
- GET /interview-bookings — List. Filters: candidate_id, vacancy_id, status (pending|confirmed|cancelled|completed), conducted (boolean).
- GET /interview-bookings/{id} — Detail with joined slot, candidate, vacancy.
- PATCH /interview-bookings/{id} — Update. Allowed: conducted (auto-sets status=completed), conduct_notes, conduct_rating (1-5), status, cancellation_reason, notes.
### google-drive (Google Drive Operations)
- POST /google-drive/resolve-folder — Find or create client folder. Required: clientName, parentFolderId. Returns folder info with match type.
- POST /google-drive/upload — Upload files to folder. Required: folderId or (clientName + parentFolderId), files[{name, mimeType, content (base64)}]. Optional: metadata, overwrite. Max 10 files, 10MB each.
- POST /google-drive/save — Resolve folder + upload in one call. Required: clientName, parentFolderId, files[...]. Optional: metadata, overwrite, dealId, conversationId, generatedBy, tags.
## Response Format
List: `{ "data": [...], "total": 342, "limit": 50, "offset": 0 }`
Detail: `{ "data": { ... } }`
Error: `{ "error": "Description" }`
## HTTP Status Codes
200 Success | 201 Created | 400 Bad request | 401 Unauthorized | 404 Not found | 405 Method not allowed | 409 Conflict (duplicate slug, `external_id_conflict` on POST /deals) | 422 Unprocessable (e.g. `unknown_stage`, `created_by_unresolved`) | 429 Rate limit | 500 Server error
## Response Headers (v2.2)
- `X-Read-Your-Writes: true` — Returned by `PATCH /deals/{id}` to confirm the response body reflects the just-persisted state.
- `X-Stage-Normalized: =>` — Returned by `PATCH /deals/{id}` when the input `stage` was rewritten by canonicalization.
- `X-Idempotent-Replay: true` — Returned by `POST /deals/{id}/activities` when the request matched an existing activity by `gmail_message_id` / `idempotency_key` / `Idempotency-Key` header (no new row was created).
## Changelog v2.2 (2026-06-03) — Highlights for Agents
P0 (must update agent prompts/SDKs):
- POST /deals now returns 409 `external_id_conflict` when `external_id` collides. Always check before retrying; use `?force=1` only with audit reason.
- PATCH /deals/{id} stage rejection responses now expose `accepted_values[]` + `canonical_stage_slugs[]`. Map your enum to those slugs.
- POST /deals/{id}/activities is idempotent — set `metadata.gmail_message_id` (preferred for email-driven), `metadata.idempotency_key`, or send header `Idempotency-Key`.
- Use the new GET /deal-pipelines/{id}/stages BEFORE writing any stage. Cache result per pipeline_id.
P1 (recommended):
- Use POST /deals/canonical-resolution (dry-run first) to resolve duplicate clusters by gmail_thread_id/contact_email. Apply only after human/agent review.
- Adopt PATCH /deals/{id}?stage_strict=1 in agent flows that must never silently accept unknown stages.
- Use GET /deals/{id}?expand=company,activities_count,last_activity_at,stage_history_summary to cut N+1 reads.
- Use POST /invoice-batches to create batches programmatically (no more UI-only requirement); pass `created_by_email` if you don't have a UUID.
## Business Rules (Auto-Fill Summary)
- candidates: etapa=triagem on create
- vacancies: status=draft on create
- employees: cargo=Colaborador, departamento=Geral, tipo_contrato=pj on create
- service-orders: status=pending, source=api, code=OS-YYYY-XXXXX on create
- tickets: status=open, ticket_number=TK-YYYYMMDD-XXXX on create
- deals: stage_entered_at on stage change + deal_activity auto-logged
- proposals: sent_at on status=ativa
- payables/receivables: status=pending, source=manual on create
- recurring-rules: is_active=true, next_due_date=start_date on create
- cashflow: read-only projection from finance_settings rates
## Unknown Resource Response
Requesting an unknown resource returns 404 with `available_resources` array listing all 28 valid resource names plus usage examples.