openapi: 3.1.0
info:
  title: Motor — API de admissão
  version: 1.0.0
  summary: O sistema de RH da empresa admite; o Motor cria o colaborador e transmite o S-2200.
  description: |
    Uma chave de integração alcança **uma empresa** e só a admissão. Ela é gerada
    e revogada pelo escritório de contabilidade que opera a empresa no Motor, e
    tem um **nome** (por exemplo, "ERP da matriz"). A admissão feita por ela é da
    **integração da empresa**, não de uma pessoa: o histórico do colaborador diz
    "Admitido pela chave de API ‹nome›", e a saída de quem gerou a chave do
    escritório não a derruba.

    O pedido é aceito de forma **assíncrona**: o `POST` cria o colaborador e
    devolve `202` com a matrícula; o envio ao eSocial acontece depois, e o
    resultado chega por aviso (webhook) e pela consulta.

    Convenções: JSON em UTF-8; datas `YYYY-MM-DD`; instantes em ISO 8601 com
    fuso; valores em decimal com ponto (`2150.00`); CPF, CNPJ, CEP e códigos só
    com dígitos. Todo objeto do pedido é **estrito**: campo que não está aqui é
    recusado com `422 CAMPO_DESCONHECIDO`. O corpo vai até 256 KB
    (`413 CORPO_GRANDE_DEMAIS`); rota que não existe responde `404 NAO_ENCONTRADA`,
    e método errado, `405 METODO_NAO_PERMITIDO` (com o cabeçalho `Allow`).

    Limite de uso: 60 chamadas por minuto por chave (`429 LIMITE_EXCEDIDO`, com
    `Retry-After`).
servers:
  - url: https://api.motorcontabil.com
    description: Produção (o ambiente do eSocial é o da empresa — ver `ambiente`)
security:
  - chaveDeIntegracao: []
tags:
  - name: Admissões
  - name: Catálogos
x-motor-codigos:
  descricao: |
    Toda recusa tem `codigo` (estável — decida por ele), `mensagem` (para
    pessoas) e `campos[]`. Com problemas de mais de um tipo no mesmo pedido, o
    código é o primeiro desta ordem: `CAMPO_DESCONHECIDO`, `DADOS_INVALIDOS`,
    `DADOS_FALTANDO`.

    A conferência garante a **forma** que o leiaute do eSocial exige; não garante
    que o eSocial aceite o evento. As regras de validação dele (o cruzamento com o
    cadastro do empregador e com eventos anteriores) só ele aplica, e a recusa
    chega depois, pelo aviso `admissao.recusada` e pela consulta.
  recusas:
    - codigo: IDEMPOTENCY_KEY_INVALIDA
      http: 400
      fazer: "O cabeçalho `Idempotency-Key` falta ou está fora da forma (8 a 100 caracteres, letras, dígitos e `. _ : -`). Nada foi gravado."
    - codigo: CHAVE_INVALIDA
      http: 401
      fazer: "Confira o cabeçalho `Authorization: Bearer <chave>` e se a chave é a de integração. Também sai quando a empresa deixou de ser atendida pelo escritório que gerou a chave."
    - codigo: CHAVE_REVOGADA
      http: 401
      fazer: Use a outra chave ativa, ou peça ao escritório uma nova.
    - codigo: EMPRESA_NAO_HABILITADA
      http: 403
      fazer: A empresa não envia ao eSocial pelo Motor, ou não envia esta admissão por ele (por exemplo, quem declara é outro sistema). Fale com o escritório. Nada foi gravado.
    - codigo: CERTIFICADO_INDISPONIVEL
      http: 403
      fazer: O certificado A1 da empresa falta ou venceu; o escritório precisa subir o novo. Nada foi gravado.
    - codigo: CHAVE_SEM_ESCOPO
      http: 403
      fazer: A chave não alcança a admissão; peça ao escritório uma chave de admissão.
    - codigo: NAO_ENCONTRADA
      http: 404
      fazer: O id não existe ou não é da empresa desta chave; também a rota que não existe na API.
    - codigo: METODO_NAO_PERMITIDO
      http: 405
      fazer: A rota existe, com outro método (o cabeçalho `Allow` diz qual).
    - codigo: TRABALHADOR_JA_ADMITIDO
      http: 409
      fazer: Já há cadastro com este CPF na empresa. Se for reenvio seu, use a mesma `Idempotency-Key`; se for readmissão, fale com o escritório.
    - codigo: IDEMPOTENCIA_CONFLITO
      http: 409
      fazer: A `Idempotency-Key` já foi usada com outro corpo. Reenvie o corpo original, ou use uma nova se for outra admissão.
    - codigo: IDEMPOTENCIA_EM_CURSO
      http: 409
      fazer: Repita em alguns segundos com a mesma `Idempotency-Key`.
    - codigo: CORPO_GRANDE_DEMAIS
      http: 413
      fazer: O corpo passa de 256 KB (uma admissão com 99 dependentes cabe com folga).
    - codigo: CAMPO_DESCONHECIDO
      http: 422
      fazer: Tire do corpo o campo apontado em `campos[]` (inclusive `matricula`).
    - codigo: DADOS_INVALIDOS
      http: 422
      fazer: Corrija os campos apontados em `campos[]` (forma, valor ou referência inexistente).
    - codigo: DADOS_FALTANDO
      http: 422
      fazer: Complete os campos apontados em `campos[]`; os de jornada e sindicato são do cadastro do escritório.
    - codigo: LIMITE_EXCEDIDO
      http: 429
      fazer: Mais de 60 chamadas por minuto com esta chave (ou credenciais recusadas demais vindas do mesmo endereço). Espere o `Retry-After`, em segundos.
    - codigo: INDISPONIVEL
      http: 503
      fazer: Falha do Motor, não sua. Repita com a mesma `Idempotency-Key`, respeitando o `Retry-After`; não haverá duplicata.
  campos:
    - codigos:
        - CPF_AUSENTE
        - CPF_INVALIDO
      campo: trabalhador.cpf
      corrige: integrador
    - codigos:
        - NOME_AUSENTE
      campo: trabalhador.nome_completo
      corrige: integrador
    - codigos:
        - SEXO_AUSENTE
      campo: trabalhador.genero
      corrige: integrador
    - codigos:
        - RACA_COR_AUSENTE
      campo: trabalhador.raca_cor
      corrige: integrador
    - codigos:
        - GRAU_INSTRUCAO_AUSENTE
      campo: trabalhador.grau_instrucao
      corrige: integrador
    - codigos:
        - DATA_NASCIMENTO_AUSENTE
      campo: trabalhador.data_nascimento
      corrige: integrador
    - codigos:
        - EMAIL_INVALIDO
      campo: trabalhador.email
      corrige: integrador
      nota: Recusado, nunca descartado em silêncio.
    - codigos:
        - LOGRADOURO_AUSENTE
        - BAIRRO_AUSENTE
        - CEP_AUSENTE
        - UF_AUSENTE
      campo: endereco.*
      corrige: integrador
    - codigos:
        - MUNICIPIO_AUSENTE
        - MUNICIPIO_INVALIDO
        - MUNICIPIO_DIVERGE_DA_UF
      campo: endereco.cod_municipio_ibge
      corrige: integrador
    - codigos:
        - DATA_ADMISSAO_AUSENTE
      campo: vinculo.data_admissao
      corrige: integrador
    - codigos:
        - CATEGORIA_AUSENTE
        - CATEGORIA_INVALIDA
      campo: vinculo.categoria
      corrige: integrador
      nota: Só `101` ou `103`.
    - codigos:
        - TP_REG_JOR_AUSENTE
        - TP_REG_JOR_INVALIDO
      campo: vinculo.tp_reg_jor
      corrige: integrador
      nota: De `1` a `4`.
    - codigos:
        - HORAS_MENSAIS_INVALIDAS
      campo: vinculo.horas_mensais
      corrige: integrador
      nota: Inteiro de 1 a 220.
    - codigos:
        - SALARIO_AUSENTE
        - SALARIO_INVALIDO
      campo: vinculo.salario_base
      corrige: integrador
      nota: Inválido quando tem mais de duas casas decimais.
    - codigos:
        - CARGO_AUSENTE
        - CBO_AUSENTE
        - CBO_FORA_DO_CATALOGO
      campo: vinculo.cargo.*
      corrige: integrador
    - codigos:
        - CARGO_SEM_CBO
      campo: vinculo.cargo.nome
      corrige: escritorio
      nota: O cargo já cadastrado está sem CBO; o escritório o corrige no cadastro do cargo.
    - codigos:
        - JORNADA_AUSENTE
        - JORNADA_NAO_ENCONTRADA
      campo: vinculo.jornada_id
      corrige: integrador
      nota: Use um id do catálogo de jornadas.
    - codigos:
        - JORNADA_SEM_HORAS
        - JORNADA_SEM_TIPO
        - JORNADA_SEM_DESCRICAO
        - JORNADA_PARCIAL_SO_DOMESTICO
      campo: vinculo.jornada_id
      corrige: escritorio
      nota: O escritório completa a jornada no cadastro dela.
    - codigos:
        - SINDICATO_AUSENTE
        - SINDICATO_NAO_ENCONTRADO
      campo: vinculo.sindicato_id
      corrige: integrador
      nota: Use um id do catálogo de sindicatos.
    - codigos:
        - SINDICATO_SEM_CNPJ
      campo: vinculo.sindicato_id
      corrige: escritorio
      nota: O escritório completa o sindicato no cadastro dele.
    - codigos:
        - CLAUSULA_ASSECURATORIA_AUSENTE
      campo: contrato.clausula_assecuratoria
      corrige: integrador
    - codigos:
        - EXPERIENCIA_ACIMA_DE_90_DIAS
      campo: contrato.dias_*
      corrige: integrador
    - codigos:
        - DATA_TERMINO_INVALIDA
      campo: contrato.data_termino
      corrige: integrador
      nota: Tem de ser igual ou posterior à admissão.
    - codigos:
        - APRENDIZ_DADOS_OBRIGATORIOS
      campo: contrato · aprendiz.modalidade
      corrige: integrador
      nota: Categoria 103 sem contrato `PRAZO_DETERMINADO` ou sem a modalidade.
    - codigos:
        - APRENDIZ_FORA_DA_CATEGORIA
      campo: aprendiz
      corrige: integrador
      nota: Enviado fora da categoria 103.
    - codigos:
        - APRENDIZ_MODALIDADE_AUSENTE
        - APRENDIZ_INVALIDO
        - APRENDIZ_ENTIDADE_AUSENTE
        - APRENDIZ_ESTABELECIMENTO_AUSENTE
        - APRENDIZ_INCOERENTE
      campo: aprendiz.*
      corrige: integrador
    - codigos:
        - APRENDIZ_CNPJ_INVALIDO
      campo: aprendiz.cnpj_entidade_qualificadora · aprendiz.cnpj_pratica
      corrige: integrador
    - codigos:
        - APRENDIZ_INSCRICAO_INVALIDA
        - APRENDIZ_INSCRICAO_DA_PROPRIA_EMPRESA
        - APRENDIZ_CPF_NAO_SUPORTADO
      campo: aprendiz.estabelecimento_da_cota
      corrige: integrador
    - codigos:
        - PCD_TIPO_OBRIGATORIO
        - PCD_COTA_INCOERENTE
        - PCD_TIPO_SEM_DEFICIENCIA
      campo: deficiencia.*
      corrige: integrador
    - codigos:
        - PCD_COTA_APRENDIZ
      campo: deficiencia.conta_na_cota
      corrige: integrador
      nota: O aprendiz não conta na cota de pessoas com deficiência.
    - codigos:
        - DEPENDENTE_TIPO_OBRIGATORIO
        - DEPENDENTE_TIPO_INVALIDO
      campo: dependentes.N.tipo
      corrige: integrador
    - codigos:
        - DEPENDENTE_NOME_OBRIGATORIO
        - DEPENDENTE_NOME_INVALIDO
      campo: dependentes.N.nome
      corrige: integrador
    - codigos:
        - DEPENDENTE_NASCIMENTO_INVALIDO
      campo: dependentes.N.data_nascimento
      corrige: integrador
    - codigos:
        - DEPENDENTE_CPF_OBRIGATORIO
        - DEPENDENTE_CPF_INVALIDO
        - DEPENDENTE_CPF_DO_TRABALHADOR
        - DEPENDENTE_CPF_DO_EMPREGADOR
        - DEPENDENTE_CPF_REPETIDO
      campo: dependentes.N.cpf
      corrige: integrador
    - codigos:
        - DEPENDENTE_DESCRICAO_OBRIGATORIA
        - DEPENDENTE_DESCRICAO_INDEVIDA
        - DEPENDENTE_DESCRICAO_INVALIDA
      campo: dependentes.N.descricao
      corrige: integrador
    - codigos:
        - DEPENDENTE_REPETIDO
        - DEPENDENTE_LIMITE
      campo: dependentes.N · dependentes
      corrige: integrador
    - codigos:
        - DATA_INVALIDA
      campo: qualquer data
      corrige: integrador
    - codigos:
        - CAMPO_OBRIGATORIO
      campo: uma parte inteira do pedido
      corrige: integrador
      nota: "`trabalhador`, `endereco`, `vinculo`, `contrato`, `contrato.tipo`, os dias do 1º período ou a data de término."
    - codigos:
        - VALOR_INVALIDO
      campo: qualquer campo
      corrige: integrador
      nota: Tipo errado (texto onde vai número, `"sim"` onde vai `true`, `null` onde se omite) ou fora das opções do contrato.
    - codigos:
        - DADOS_S2200_FALTANDO
      campo: (corpo)
      corrige: depende
      nota: O que o S-2200 exige e não tem código próprio; a `mensagem` diz o quê. Na dúvida, o escritório.
    - codigos:
        - JSON_INVALIDO
      campo: (corpo)
      corrige: integrador
  avisos:
    - codigo: CBO_DO_CADASTRO
      nota: O cargo já existia com CBO diferente do informado; vale o do cadastro.
paths:
  /v1/admissoes:
    post:
      tags:
        - Admissões
      operationId: criarAdmissao
      x-motor-menu: Enviar admissão
      summary: Envia uma admissão
      description: |
        Confere o pedido inteiro **antes de gravar**: forma, referências
        (jornada, sindicato, CBO, município) e tudo o que o S-2200 exige. Se algo
        faltar ou estiver inválido, responde `422` e **nada é gravado**.

        Passando, cria o colaborador e o vínculo numa transação só, gera a
        matrícula e responde `202`. O envio ao eSocial segue em segundo plano.

        O cabeçalho `Idempotency-Key` é obrigatório, e ela vale **por empresa**:
        a mesma `Idempotency-Key` com o mesmo corpo devolve a mesma resposta e não
        cria nada — **mesmo que o reenvio venha por outra chave de integração
        ativa da mesma empresa** (quem troca de chave no meio e reenvia recebe a
        resposta guardada); com outro corpo, `409 IDEMPOTENCIA_CONFLITO`; com o
        primeiro pedido ainda em processamento, `409 IDEMPOTENCIA_EM_CURSO`. A
        `Idempotency-Key` só fica presa a um pedido quando a resposta é `202` — uma
        recusa `4xx` ou `503` não a consome.

        A admissão entra na contagem de colaboradores ativos do escritório, como
        a feita pela tela; não há cobrança própria por chamada.

        Escritório suspenso **não** recusa a entrada: a admissão é aceita e o
        evento montado, mas espera na fila do escritório, e o aviso
        `admissao.aguardando_escritorio` diz isso.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AdmissaoPedido"
            examples:
              experiencia:
                summary: Contrato de experiência de 45 dias, sem deficiência nem dependentes
                value:
                  trabalhador:
                    nome_completo: Maria Aparecida Souza
                    cpf: "52998224725"
                    data_nascimento: 1998-03-14
                    genero: FEMININO
                    raca_cor: PARDA
                    grau_instrucao: 7
                    estado_civil: SOLTEIRO
                    email: maria.souza@example.com
                    celular: "62991234567"
                  endereco:
                    cep: "74000000"
                    logradouro: Rua 3
                    numero: "120"
                    bairro: Setor Central
                    cod_municipio_ibge: "5208707"
                    uf: GO
                  vinculo:
                    data_admissao: 2026-11-03
                    categoria: 101
                    salario_base: 2150
                    tp_reg_jor: 1
                    cargo:
                      nome: Auxiliar administrativo
                      cbo: "411005"
                    jornada_id: 6f1c2a1e-1111-4c4c-9a9a-000000000001
                    sindicato_id: 0b7e9d2c-2222-4d4d-8b8b-000000000002
                  contrato:
                    tipo: EXPERIENCIA
                    dias_primeiro_periodo: 45
                    dias_segundo_periodo: 45
                    clausula_assecuratoria: false
              aprendiz_com_dependente:
                summary: Aprendiz (categoria 103), contratação direta, com um dependente
                value:
                  trabalhador:
                    nome_completo: João Pedro Lima
                    cpf: "11144477735"
                    data_nascimento: 2009-07-02
                    genero: MASCULINO
                    raca_cor: BRANCA
                    grau_instrucao: 6
                  endereco:
                    cep: "74000000"
                    logradouro: Avenida Goiás
                    bairro: Setor Central
                    cod_municipio_ibge: "5208707"
                    uf: GO
                  vinculo:
                    data_admissao: 2026-11-03
                    categoria: 103
                    salario_base: 1100
                    tp_reg_jor: 1
                    cargo:
                      nome: Aprendiz de auxiliar administrativo
                      cbo: "411005"
                    jornada_id: 6f1c2a1e-1111-4c4c-9a9a-000000000003
                    sindicato_id: 0b7e9d2c-2222-4d4d-8b8b-000000000002
                  contrato:
                    tipo: PRAZO_DETERMINADO
                    data_termino: 2028-11-02
                    clausula_assecuratoria: false
                  aprendiz:
                    modalidade: DIRETA
                    cnpj_entidade_qualificadora: "33641358000152"
                  dependentes:
                    - tipo: "03"
                      nome: Ana Lima
                      data_nascimento: 2026-01-20
                      cpf: "39053344705"
                      dependente_irrf: true
                      dependente_salario_familia: true
      responses:
        "202":
          description: |
            Admissão aceita. O colaborador já existe no Motor e a matrícula é
            definitiva — guarde-a. Repetida pela mesma `Idempotency-Key` com o
            mesmo corpo, volta exatamente esta resposta, com o cabeçalho
            `X-Motor-Repetido: true`.
          headers:
            X-Motor-Repetido:
              description: "`true` quando a resposta é a repetição de um pedido já aceito."
              schema:
                type: string
                enum:
                  - "true"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdmissaoAceita"
              example:
                id: 9a3f0c55-7d7e-4a52-b8c1-5d9f3c2e1a10
                estado: RECEBIDA
                matricula: MOT-000042
                ambiente: PRODUCAO
                prazo_legal: 2026-11-02
                fora_do_prazo: false
                recebida_em: 2026-10-28T13:05:12-03:00
                avisos: []
        "400":
          description: |
            `IDEMPOTENCY_KEY_INVALIDA` — o cabeçalho `Idempotency-Key` falta ou está
            fora da forma (8 a 100 caracteres: letras, dígitos e `. _ : -`). Nada foi
            gravado nem consumido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Erro"
        "401":
          $ref: "#/components/responses/NaoAutenticado"
        "403":
          $ref: "#/components/responses/EmpresaImpedida"
        "409":
          description: |
            `TRABALHADOR_JA_ADMITIDO` — já existe colaborador com este CPF nesta
            empresa (inclusive desligado; readmissão está fora da v1).
            `IDEMPOTENCIA_CONFLITO` — a `Idempotency-Key` já foi usada nesta empresa
            com outro corpo.
            `IDEMPOTENCIA_EM_CURSO` — um pedido com a mesma `Idempotency-Key` ainda
            está sendo processado nesta empresa; repita em alguns segundos.
          headers:
            Retry-After:
              description: Só em `IDEMPOTENCIA_EM_CURSO`, em segundos.
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Erro"
        "413":
          description: "`CORPO_GRANDE_DEMAIS` — o corpo passa de 256 KB. Nada foi gravado."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Erro"
        "422":
          $ref: "#/components/responses/DadosRecusados"
        "429":
          $ref: "#/components/responses/LimiteExcedido"
        "503":
          $ref: "#/components/responses/Indisponivel"
  /v1/admissoes/{id}:
    get:
      tags:
        - Admissões
      operationId: consultarAdmissao
      x-motor-menu: Consultar admissão
      summary: Consulta o andamento de uma admissão
      description: |
        Alcança as admissões da **empresa da chave**, com qualquer chave ativa
        dela — inclusive a admissão enviada por uma chave já revogada, para que a
        troca de chave não deixe nenhuma sem consulta. Admissão de outra empresa e
        admissão inexistente respondem igual: `404 NAO_ENCONTRADA`.

        `xml_evento` é o XML **que foi enviado** ao eSocial — pode diferir do
        pedido, porque o escritório pode corrigir o evento antes de aprovar. Trate
        o XML devolvido como a verdade.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Andamento atual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdmissaoAndamento"
        "401":
          $ref: "#/components/responses/NaoAutenticado"
        "404":
          $ref: "#/components/responses/NaoEncontrada"
        "429":
          $ref: "#/components/responses/LimiteExcedido"
        "503":
          $ref: "#/components/responses/Indisponivel"
  /v1/catalogos/jornadas:
    get:
      tags:
        - Catálogos
      operationId: listarJornadas
      x-motor-menu: Jornadas
      summary: Lista as jornadas ativas da empresa
      description: |
        As jornadas são cadastradas pelo escritório no Motor. Use o `id` em
        `vinculo.jornada_id`. Uma jornada com `pronta_para_esocial = false` é
        aceita no catálogo mas recusaria a admissão (`422`, códigos
        `JORNADA_SEM_*`) — quem a completa é o escritório.
      responses:
        "200":
          description: Jornadas ativas.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - itens
                properties:
                  itens:
                    type: array
                    items:
                      $ref: "#/components/schemas/Jornada"
        "401":
          $ref: "#/components/responses/NaoAutenticado"
        "429":
          $ref: "#/components/responses/LimiteExcedido"
        "503":
          $ref: "#/components/responses/Indisponivel"
  /v1/catalogos/sindicatos:
    get:
      tags:
        - Catálogos
      operationId: listarSindicatos
      x-motor-menu: Sindicatos
      summary: Lista os sindicatos ativos da empresa
      description: |
        Os sindicatos são cadastrados pelo escritório no Motor. Use o `id` em
        `vinculo.sindicato_id`. Sindicato sem CNPJ não monta o S-2200
        (`SINDICATO_SEM_CNPJ`) — quem o completa é o escritório.
      responses:
        "200":
          description: Sindicatos ativos.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - itens
                properties:
                  itens:
                    type: array
                    items:
                      $ref: "#/components/schemas/Sindicato"
        "401":
          $ref: "#/components/responses/NaoAutenticado"
        "429":
          $ref: "#/components/responses/LimiteExcedido"
        "503":
          $ref: "#/components/responses/Indisponivel"
webhooks:
  admissao.processada:
    post:
      summary: O eSocial aceitou o S-2200
      description: O recibo está em `admissao.nr_recibo`. Os XML vêm pela consulta.
      parameters:
        - $ref: "#/components/parameters/AvisoId"
        - $ref: "#/components/parameters/AvisoEvento"
        - $ref: "#/components/parameters/AvisoAssinatura"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Aviso"
      responses:
        2XX:
          description: Recebido. Qualquer outra resposta, ou nenhuma em 10 s, gera nova tentativa.
  admissao.recusada:
    post:
      summary: O eSocial recusou o S-2200
      description: |
        As ocorrências do eSocial estão em `admissao.ocorrencias`. O escritório
        também é avisado. O envio automático **nunca reenvia**. Se o escritório
        corrigir e reenviar, a mesma admissão **pode** voltar a `ENVIADA` e gerar
        novo aviso no fim: não trate `recusada` como final.
      parameters:
        - $ref: "#/components/parameters/AvisoId"
        - $ref: "#/components/parameters/AvisoEvento"
        - $ref: "#/components/parameters/AvisoAssinatura"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Aviso"
      responses:
        2XX:
          description: Recebido. Qualquer outra resposta, ou nenhuma em 10 s, gera nova tentativa.
  admissao.descartada:
    post:
      summary: O escritório descartou o evento antes de enviá-lo
      description: |
        O S-2200 não foi e não será enviado por esta admissão, salvo se o
        escritório o restaurar. O colaborador continua cadastrado no Motor.
      parameters:
        - $ref: "#/components/parameters/AvisoId"
        - $ref: "#/components/parameters/AvisoEvento"
        - $ref: "#/components/parameters/AvisoAssinatura"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Aviso"
      responses:
        2XX:
          description: Recebido. Qualquer outra resposta, ou nenhuma em 10 s, gera nova tentativa.
  admissao.aguardando_escritorio:
    post:
      summary: O envio automático parou e o evento está na fila do escritório
      description: |
        Só sai quando o envio automático estava ligado na chegada da admissão e
        para no meio: o evento não montou; o envio automático foi desligado; a
        admissão foi alterada no Motor depois de recebida; o envio teve erro
        técnico, o certificado não pôde ser usado ou o eSocial precisa de outro
        evento antes deste (o envio automático **nunca** tenta de novo); o
        escritório está suspenso; ou uma falha do Motor impediu o envio automático
        de terminar. `admissao.motivo` diz qual, em texto para pessoas — não decida
        por ele. Também sai
        quando a **chave que enviou a admissão é revogada** antes do envio: revogar existe para cortar um canal que pode
        estar comprometido, então o que entrou por ele não é transmitido sem que
        uma pessoa do escritório aprove. Com o envio automático desligado,
        esperar o escritório é o normal e este aviso **não** sai.

        O aviso vai ao endereço do webhook da **integração da empresa**, que não
        muda com a troca de chave. Sem endereço cadastrado, os avisos não saem (o
        andamento fica na consulta); cadastrado depois, só os avisos novos saem.
        Redirecionamento não é seguido (`3xx` conta como falha de entrega).
      parameters:
        - $ref: "#/components/parameters/AvisoId"
        - $ref: "#/components/parameters/AvisoEvento"
        - $ref: "#/components/parameters/AvisoAssinatura"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Aviso"
      responses:
        2XX:
          description: Recebido. Qualquer outra resposta, ou nenhuma em 10 s, gera nova tentativa.
components:
  securitySchemes:
    chaveDeIntegracao:
      type: http
      scheme: bearer
      description: |
        `Authorization: Bearer <chave>`. A chave é gerada pelo escritório na
        ficha da empresa, com um nome, aparece uma vez (o Motor guarda só o
        resumo SHA-256 dela), e alcança uma empresa e só a admissão. Vale
        enquanto não for revogada e o escritório que a gerou atender a empresa.
        A empresa pode ter **até duas chaves ativas** ao mesmo tempo,
        para trocar sem parar; o endereço do webhook e o segredo dele são da
        integração da empresa, não de cada chave — o webhook é opcional, e trocar
        o endereço gera um segredo novo na hora. Sessão de usuário e token OAuth
        são recusados nestas rotas.
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: |
        Identificador único deste pedido, gerado pelo seu sistema (um UUID serve).
        Reenvie com a mesma chave quando não souber se o pedido anterior chegou.
      schema:
        type: string
        minLength: 8
        maxLength: 100
        pattern: ^[A-Za-z0-9._:-]+$
    AvisoId:
      name: X-Motor-Aviso-Id
      in: header
      required: true
      description: Id do aviso, igual ao `id` do corpo. Use para descartar duplicatas.
      schema:
        type: string
        format: uuid
    AvisoEvento:
      name: X-Motor-Evento
      in: header
      required: true
      description: O que aconteceu, igual ao `evento` do corpo.
      schema:
        type: string
        enum:
          - admissao.processada
          - admissao.recusada
          - admissao.descartada
          - admissao.aguardando_escritorio
        x-enumDescriptions:
          admissao.processada: O eSocial aceitou o S-2200.
          admissao.recusada: O eSocial recusou o S-2200.
          admissao.descartada: O escritório descartou o evento antes de enviá-lo.
          admissao.aguardando_escritorio: O envio automático parou e o evento está na fila do escritório.
    AvisoAssinatura:
      name: X-Motor-Assinatura
      in: header
      required: true
      description: |
        `t=<unix>,v1=<hex>`, onde `v1` é o HMAC-SHA256, em hexadecimal minúsculo,
        de `t + "." + corpo cru`, com o segredo do webhook (texto UTF-8, exatamente
        como foi mostrado) como chave. Confira sobre os BYTES recebidos, antes de
        interpretar o JSON.
      schema:
        type: string
        pattern: ^t=\d+,v1=[0-9a-f]{64}$
  responses:
    NaoAutenticado:
      description: |
        `CHAVE_INVALIDA` — ausente, malformada, desconhecida, credencial que não
        é chave de integração, ou chave de uma empresa que deixou de ser
        atendida pelo escritório que a gerou. `CHAVE_REVOGADA` — a chave foi revogada pelo
        escritório; vale a partir da chamada seguinte à revogação. Admissão
        enviada por ela e ainda não transmitida vai para a fila do escritório
        (aviso `admissao.aguardando_escritorio`).
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Erro"
    EmpresaImpedida:
      description: |
        A chave é válida, mas a empresa não pode admitir pela API agora.
        `EMPRESA_NAO_HABILITADA` — a empresa não está habilitada no eSocial pelo Motor.
        `CERTIFICADO_INDISPONIVEL` — sem certificado A1 válido no cofre (ausente ou vencido).
        `CHAVE_SEM_ESCOPO` — a chave não alcança a admissão.
        Nada foi gravado. Quem resolve é o escritório.

        Escritório suspenso **não** é recusa de entrada: com o escritório suspenso a admissão é aceita (`202`), o
        evento é montado e espera na fila do escritório, e sai o aviso
        `admissao.aguardando_escritorio`.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Erro"
    DadosRecusados:
      description: |
        Nada foi gravado. `campos[]` diz o que corrigir, campo a campo.
        `CAMPO_DESCONHECIDO` — o corpo tem campo que este contrato não define
        (inclusive `matricula`, que é gerada pelo Motor).
        `DADOS_INVALIDOS` — campo com forma ou valor inválido, ou referência que
        não existe (jornada, sindicato, CBO, município).
        `DADOS_FALTANDO` — falta o que o S-2200 exige.
        Quando há problemas de mais de um tipo, o código é o primeiro desta
        ordem: CAMPO_DESCONHECIDO, DADOS_INVALIDOS, DADOS_FALTANDO.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Erro"
          example:
            codigo: DADOS_FALTANDO
            mensagem: O S-2200 não pode ser montado com estes dados.
            campos:
              - campo: vinculo.jornada_id
                codigo: JORNADA_AUSENTE
                mensagem: Informe a jornada (obrigatória quando tp_reg_jor = 1).
              - campo: trabalhador.grau_instrucao
                codigo: GRAU_INSTRUCAO_AUSENTE
                mensagem: Informe o grau de instrução.
    NaoEncontrada:
      description: "`NAO_ENCONTRADA` — não existe, ou não é da empresa desta chave."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Erro"
    LimiteExcedido:
      description: |
        `LIMITE_EXCEDIDO` — mais de 60 chamadas por minuto com esta chave. Só
        isso: a admissão pela API não tem limite próprio no plano do escritório
        (conta como colaborador ativo, como a da tela). Espere o `Retry-After`
        (em segundos).
      headers:
        Retry-After:
          description: Segundos a esperar antes de repetir.
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Erro"
    Indisponivel:
      description: |
        `INDISPONIVEL` — falha nossa, não do pedido nem da chave. No `POST`,
        repita com a MESMA `Idempotency-Key`, respeitando `Retry-After` quando
        vier: não haverá duplicata — se a admissão tiver sido criada, a
        repetição devolve o `202` dela.
      headers:
        Retry-After:
          description: Segundos a esperar antes de repetir, quando vier.
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Erro"
  schemas:
    AdmissaoPedido:
      type: object
      additionalProperties: false
      required:
        - trabalhador
        - endereco
        - vinculo
        - contrato
      properties:
        trabalhador:
          $ref: "#/components/schemas/Trabalhador"
        endereco:
          $ref: "#/components/schemas/Endereco"
        vinculo:
          $ref: "#/components/schemas/Vinculo"
        contrato:
          $ref: "#/components/schemas/Contrato"
        deficiencia:
          $ref: "#/components/schemas/Deficiencia"
        aprendiz:
          $ref: "#/components/schemas/Aprendiz"
        dependentes:
          type: array
          maxItems: 99
          items:
            $ref: "#/components/schemas/Dependente"
          description: |
            Até 99 (`DEPENDENTE_LIMITE`). Não pode haver dois com o mesmo CPF
            (`DEPENDENTE_CPF_REPETIDO`), nem dois com o mesmo `tipo` + `nome` +
            `data_nascimento` (`DEPENDENTE_REPETIDO`). Os dependentes marcados
            `dependente_irrf` são a contagem que o Motor usa na dedução do IRRF —
            não há campo de "número de dependentes".
      allOf:
        - if:
            properties:
              vinculo:
                properties:
                  categoria:
                    const: 103
                required:
                  - categoria
          then:
            required:
              - aprendiz
            properties:
              contrato:
                properties:
                  tipo:
                    const: PRAZO_DETERMINADO
          else:
            not:
              required:
                - aprendiz
      description: |
        Escopo da v1: empregado CLT (categoria 101) por prazo indeterminado, em
        contrato de experiência ou por prazo determinado; aprendiz (103); com ou
        sem deficiência e dependentes. Não há campo de matrícula nem de empresa:
        a empresa é a da chave, e a matrícula é gerada pelo Motor.
    Trabalhador:
      type: object
      additionalProperties: false
      required:
        - nome_completo
        - cpf
        - data_nascimento
        - genero
        - raca_cor
        - grau_instrucao
      properties:
        nome_completo:
          type: string
          minLength: 2
          maxLength: 70
          pattern: ^.*\S.*$
          description: Uma linha só, com ao menos um caractere visível (`TS_nome` do XSD).
        cpf:
          type: string
          pattern: ^\d{11}$
          description: Só dígitos. Os dígitos verificadores são conferidos (`CPF_INVALIDO`).
        data_nascimento:
          type: string
          format: date
        genero:
          type: string
          enum:
            - MASCULINO
            - FEMININO
          description: Sexo declarado ao eSocial (o leiaute só aceita M ou F).
          x-enumDescriptions:
            MASCULINO: Masculino (M no eSocial)
            FEMININO: Feminino (F no eSocial)
        raca_cor:
          type: string
          enum:
            - INDIGENA
            - BRANCA
            - PRETA
            - AMARELA
            - PARDA
          description: |
            Por nome, não por código: a ordem do cadastro do Motor difere da
            tabela do eSocial, e o nome não deixa margem. "Não informado" não
            existe aqui — o eSocial o recusa para admissões desde 22/04/2024.
          x-enumDescriptions:
            INDIGENA: Indígena (código 5 no eSocial)
            BRANCA: Branca (código 1 no eSocial)
            PRETA: Preta (código 2 no eSocial)
            AMARELA: Amarela (código 4 no eSocial)
            PARDA: Parda (código 3 no eSocial)
        grau_instrucao:
          type: integer
          enum:
            - 1
            - 2
            - 3
            - 4
            - 5
            - 6
            - 7
            - 8
            - 9
            - 10
            - 11
            - 12
          description: Código do grau de instrução do eSocial (01 a 12), sem o zero à esquerda.
          x-enumDescriptions:
            "1": Analfabeto, inclusive o que, embora tenha recebido instrução, não se alfabetizou
            "2": Até o 5º ano incompleto do ensino fundamental (antiga 4ª série) ou que se tenha alfabetizado sem ter frequentado escola regular
            "3": 5º ano completo do ensino fundamental
            "4": Do 6º ao 9º ano do ensino fundamental incompleto (antiga 5ª a 8ª série)
            "5": Ensino fundamental completo
            "6": Ensino médio incompleto
            "7": Ensino médio completo
            "8": Educação superior incompleta
            "9": Educação superior completa
            "10": Pós-graduação completa
            "11": Mestrado completo
            "12": Doutorado completo
        estado_civil:
          type: string
          enum:
            - SOLTEIRO
            - CASADO
            - DIVORCIADO
            - VIUVO
            - SEPARADO
            - UNIAO_ESTAVEL
          description: Gravado no cadastro. Hoje o S-2200 do Motor não o declara.
          x-enumDescriptions:
            SOLTEIRO: Solteiro(a)
            CASADO: Casado(a)
            DIVORCIADO: Divorciado(a)
            VIUVO: Viúvo(a)
            SEPARADO: Separado(a)
            UNIAO_ESTAVEL: União estável
        nome_mae:
          type: string
          minLength: 2
          maxLength: 70
        nome_pai:
          type: string
          minLength: 2
          maxLength: 70
        email:
          type: string
          format: email
          minLength: 6
          maxLength: 60
          pattern: ^[^@\s]+@[^@\s]+\.[^@\s]+$
          description: |
            Fora deste formato é recusado com `422 DADOS_INVALIDOS`
            (`EMAIL_INVALIDO`) — nunca descartado em silêncio.
        celular:
          type: string
          pattern: ^\d{10,13}$
          description: Com DDD, só dígitos.
    Endereco:
      type: object
      additionalProperties: false
      required:
        - cep
        - logradouro
        - bairro
        - cod_municipio_ibge
        - uf
      properties:
        cep:
          type: string
          pattern: ^\d{8}$
        logradouro:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^\S[\s\S]*$
          description: O primeiro caractere não pode ser espaço (`TS_texto_100` do XSD).
        numero:
          type: string
          minLength: 1
          maxLength: 10
          pattern: ^.*\S.*$
          description: Uma linha, com ao menos um caractere visível (`TS_nrLograd`). Ausente, o Motor declara "S/N".
        complemento:
          type: string
          minLength: 1
          maxLength: 30
          pattern: ^.*\S.*$
          description: Uma linha, com ao menos um caractere visível (`TS_complemento`).
        bairro:
          type: string
          minLength: 1
          maxLength: 90
          pattern: ^.*\S.*$
          description: Uma linha, com ao menos um caractere visível (`TS_bairro`).
        cod_municipio_ibge:
          type: string
          pattern: ^\d{7}$
          description: |
            Código IBGE do município (7 dígitos). Precisa existir
            (`MUNICIPIO_INVALIDO`) e ser da `uf` informada (`MUNICIPIO_DIVERGE_DA_UF`).
        uf:
          type: string
          enum:
            - AC
            - AL
            - AM
            - AP
            - BA
            - CE
            - DF
            - ES
            - GO
            - MA
            - MG
            - MS
            - MT
            - PA
            - PB
            - PE
            - PI
            - PR
            - RJ
            - RN
            - RO
            - RR
            - RS
            - SC
            - SE
            - SP
            - TO
          x-enumDescriptions:
            AC: Acre
            AL: Alagoas
            AM: Amazonas
            AP: Amapá
            BA: Bahia
            CE: Ceará
            DF: Distrito Federal
            ES: Espírito Santo
            GO: Goiás
            MA: Maranhão
            MG: Minas Gerais
            MS: Mato Grosso do Sul
            MT: Mato Grosso
            PA: Pará
            PB: Paraíba
            PE: Pernambuco
            PI: Piauí
            PR: Paraná
            RJ: Rio de Janeiro
            RN: Rio Grande do Norte
            RO: Rondônia
            RR: Roraima
            RS: Rio Grande do Sul
            SC: Santa Catarina
            SE: Sergipe
            SP: São Paulo
            TO: Tocantins
    Vinculo:
      type: object
      additionalProperties: false
      required:
        - data_admissao
        - categoria
        - salario_base
        - tp_reg_jor
        - cargo
        - sindicato_id
      properties:
        data_admissao:
          type: string
          format: date
          description: |
            O prazo do S-2200 é a véspera. Data de hoje ou passada é aceita e
            enviada; a resposta diz `fora_do_prazo: true`.
        categoria:
          type: integer
          enum:
            - 101
            - 103
          description: Categoria do trabalhador (Tabela 01 do eSocial).
          x-enumDescriptions:
            "101": Empregado - Geral, inclusive o empregado público da administração direta ou indireta contratado pela CLT
            "103": Empregado - Aprendiz
        salario_base:
          type: number
          exclusiveMinimum: 0
          description: |
            Salário mensal, em reais, com no máximo duas casas decimais. Mais que
            isso é recusado pelo servidor com `422 DADOS_INVALIDOS`
            (`SALARIO_INVALIDO`). A regra não está no schema porque `multipleOf:
            0.01` reprova valores válidos por arredondamento binário (1234.56).
        horas_mensais:
          type: integer
          minimum: 1
          maximum: 220
          description: |
            Divisor do salário-hora na folha (220 = 44 h semanais). Não é a
            jornada: o que o S-2200 declara sai da jornada do catálogo. Ausente,
            o Motor o deriva da jornada escolhida (horas semanais × 5, até 220);
            sem jornada (`tp_reg_jor` 2, 3 ou 4), 220. Fora de 1 a 220:
            `HORAS_MENSAIS_INVALIDAS`.
        tp_reg_jor:
          type: integer
          enum:
            - 1
            - 2
            - 3
            - 4
          description: Regime de jornada do eSocial. O 1 exige `jornada_id`.
          x-enumDescriptions:
            "1": Submetido a horário de trabalho (Capítulo II do Título II da CLT)
            "2": Atividade externa especificada no inciso I do art. 62 da CLT
            "3": Função especificada no inciso II do art. 62 da CLT
            "4": Teletrabalho, previsto no inciso III do art. 62 da CLT
        cargo:
          $ref: "#/components/schemas/Cargo"
        jornada_id:
          type: string
          format: uuid
          description: Id de `GET /v1/catalogos/jornadas`. Obrigatório quando `tp_reg_jor` = 1.
        sindicato_id:
          type: string
          format: uuid
          description: Id de `GET /v1/catalogos/sindicatos`.
      allOf:
        - if:
            properties:
              tp_reg_jor:
                const: 1
            required:
              - tp_reg_jor
          then:
            required:
              - jornada_id
    Cargo:
      type: object
      additionalProperties: false
      required:
        - nome
        - cbo
      description: |
        Descrito, não referenciado. O Motor procura um cargo com o mesmo nome
        (sem diferenciar maiúsculas) no cadastro da empresa e o cria, com o CBO
        informado, se não houver.

        Se o cargo **já existir**, vale o CBO do cadastro e o informado é
        ignorado — cargo é cadastro da empresa, e trocar o CBO dele mudaria o de
        todos os colaboradores daquele cargo. O aviso `CBO_DO_CADASTRO` sai
        **só quando o CBO informado difere do cadastro**. Se o cargo
        existente estiver **sem CBO**, a admissão é recusada com
        `CARGO_SEM_CBO`, e quem corrige é o **escritório**, no cadastro do cargo
        — o CBO informado não o preenche.
      properties:
        nome:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^.*\S.*$
        cbo:
          type: string
          pattern: ^\d{6}$
          description: Código da CBO. Precisa existir na classificação (`CBO_FORA_DO_CATALOGO`).
    Contrato:
      oneOf:
        - $ref: "#/components/schemas/ContratoIndeterminado"
        - $ref: "#/components/schemas/ContratoExperiencia"
        - $ref: "#/components/schemas/ContratoPrazoDeterminado"
      discriminator:
        propertyName: tipo
        mapping:
          INDETERMINADO: "#/components/schemas/ContratoIndeterminado"
          EXPERIENCIA: "#/components/schemas/ContratoExperiencia"
          PRAZO_DETERMINADO: "#/components/schemas/ContratoPrazoDeterminado"
    ContratoIndeterminado:
      type: object
      additionalProperties: false
      required:
        - tipo
      properties:
        tipo:
          const: INDETERMINADO
    ContratoExperiencia:
      type: object
      additionalProperties: false
      required:
        - tipo
        - dias_primeiro_periodo
        - clausula_assecuratoria
      description: |
        Contrato de experiência é prazo determinado para o eSocial: o S-2200
        declara o término do 1º período (o dia da admissão conta como dia 1). A
        prorrogação para o 2º período é ato do escritório no Motor (S-2206) e não
        passa por esta API. A soma dos dois períodos não passa de 90 dias
        (`EXPERIENCIA_ACIMA_DE_90_DIAS`).
      properties:
        tipo:
          const: EXPERIENCIA
        dias_primeiro_periodo:
          type: integer
          minimum: 1
          maximum: 90
        dias_segundo_periodo:
          type:
            - integer
            - "null"
          minimum: 1
          maximum: 89
          description: Planejado, não declarado. Nulo ou ausente = ainda não decidido.
        clausula_assecuratoria:
          type: boolean
          description: Cláusula assecuratória do direito recíproco de rescisão antecipada (art. 481 da CLT).
    ContratoPrazoDeterminado:
      type: object
      additionalProperties: false
      required:
        - tipo
        - data_termino
        - clausula_assecuratoria
      description: |
        Contrato por prazo determinado, para a categoria 101 ou 103, com a data de
        término obrigatória. Obrigatório para o aprendiz (categoria 103) — sem ele,
        `APRENDIZ_DADOS_OBRIGATORIOS`.
      properties:
        tipo:
          const: PRAZO_DETERMINADO
        data_termino:
          type: string
          format: date
          description: Igual ou posterior à `data_admissao` (`DATA_TERMINO_INVALIDA`).
        clausula_assecuratoria:
          type: boolean
    Deficiencia:
      type: object
      additionalProperties: false
      required:
        - conta_na_cota
      description: |
        Envie só para pessoa com deficiência ou reabilitada: pelo menos um dos
        seis indicadores tem de ser `true` (`PCD_TIPO_OBRIGATORIO`). Ausente, o
        grupo não é declarado. Os indicadores são só `true`, `false` ou
        ausentes (texto como "sim" é recusado, nunca convertido).

        Recusas, em `campos[]`: `PCD_TIPO_OBRIGATORIO` (nenhum indicador
        verdadeiro); `PCD_COTA_INCOERENTE` (`conta_na_cota` sem deficiência nem
        reabilitação, ou faltando com elas); `PCD_COTA_APRENDIZ` (aprendiz —
        categoria 103 — com `conta_na_cota: true`: o aprendiz com deficiência não
        conta na cota, MOS §23.6); `PCD_TIPO_SEM_DEFICIENCIA` (tipo de deficiência
        ou observação sem a deficiência).
      properties:
        fisica:
          type: boolean
          default: false
        visual:
          type: boolean
          default: false
        auditiva:
          type: boolean
          default: false
        mental:
          type: boolean
          default: false
        intelectual:
          type: boolean
          default: false
        reabilitado:
          type: boolean
          default: false
          description: Beneficiário reabilitado ou readaptado.
        conta_na_cota:
          type: boolean
          description: |
            Conta na cota de pessoas com deficiência (Lei 8.213/1991)? Pode ser
            `false` com deficiência — a pessoa pode recusar a ação afirmativa. Tem
            de ser `false` para o aprendiz (`PCD_COTA_APRENDIZ`).
        observacao:
          type: string
          minLength: 1
          maxLength: 255
          pattern: ^\S[\s\S]*$
          description: O primeiro caractere não pode ser espaço (`TS_texto_255` do XSD).
      anyOf:
        - required:
            - fisica
          properties:
            fisica:
              const: true
        - required:
            - visual
          properties:
            visual:
              const: true
        - required:
            - auditiva
          properties:
            auditiva:
              const: true
        - required:
            - mental
          properties:
            mental:
              const: true
        - required:
            - intelectual
          properties:
            intelectual:
              const: true
        - required:
            - reabilitado
          properties:
            reabilitado:
              const: true
    Aprendiz:
      type: object
      additionalProperties: false
      required:
        - modalidade
      description: |
        Obrigatório, e só aceito, quando `vinculo.categoria` = 103: fora dela,
        `APRENDIZ_FORA_DA_CATEGORIA`; na 103 sem a modalidade, ou sem contrato
        `PRAZO_DETERMINADO`, `APRENDIZ_DADOS_OBRIGATORIOS` (o grupo do aprendiz
        é obrigatório no S-2200 desde 22/01/2024).

        Recusas, em `campos[]`, com o código do servidor:
        `APRENDIZ_MODALIDADE_AUSENTE` · `APRENDIZ_INVALIDO` (forma) ·
        `APRENDIZ_ENTIDADE_AUSENTE` (DIRETA sem a entidade qualificadora) ·
        `APRENDIZ_ESTABELECIMENTO_AUSENTE` (INDIRETA sem o estabelecimento) ·
        `APRENDIZ_INCOERENTE` (campo da outra modalidade) ·
        `APRENDIZ_CNPJ_INVALIDO` (entidade qualificadora ou prática) ·
        `APRENDIZ_INSCRICAO_INVALIDA` (estabelecimento da cota) ·
        `APRENDIZ_INSCRICAO_DA_PROPRIA_EMPRESA` · `APRENDIZ_CPF_NAO_SUPORTADO`.
      properties:
        modalidade:
          type: string
          enum:
            - DIRETA
            - INDIRETA
          description: Como o aprendiz é contratado.
          x-enumDescriptions:
            DIRETA: A própria empresa da chave cumpre a cota e contrata o aprendiz (exige `cnpj_entidade_qualificadora`).
            INDIRETA: A empresa da chave é a entidade sem fins lucrativos ou de prática desportiva que contrata o aprendiz para outro estabelecimento cumprir a cota (exige `estabelecimento_da_cota`).
        cnpj_entidade_qualificadora:
          type: string
          pattern: ^\d{14}$
          description: |
            CNPJ da entidade qualificadora, na contratação direta. Os dígitos
            verificadores são conferidos pelo servidor (`APRENDIZ_CNPJ_INVALIDO`).
        estabelecimento_da_cota:
          description: |
            Só na contratação INDIRETA: o estabelecimento **para o qual** a
            contratação foi efetivada — quem cumpre a cota com este aprendiz.
            **Não é a empresa da chave**: a raiz do CNPJ (8 primeiros dígitos)
            tem de ser diferente da do empregador
            (`APRENDIZ_INSCRICAO_DA_PROPRIA_EMPRESA`). Os dígitos verificadores
            são conferidos pelo servidor (`APRENDIZ_INSCRICAO_INVALIDA`).

            **Só CNPJ.** O leiaute admite CPF, mas a biblioteca do eSocial não o
            aceita nas alterações contratuais (S-2206) deste aprendiz: com CPF,
            todo reajuste futuro ficaria travado. CPF é recusado com
            `APRENDIZ_CPF_NAO_SUPORTADO` até a biblioteca aceitá-lo.
          type: object
          additionalProperties: false
          required:
            - tipo_inscricao
            - numero
          properties:
            tipo_inscricao:
              const: CNPJ
            numero:
              type: string
              pattern: ^\d{14}$
        cnpj_pratica:
          type: string
          pattern: ^\d{14}$
          description: |
            Estabelecimento onde acontecem as atividades práticas, quando for
            outro. Os dígitos verificadores são conferidos pelo servidor
            (`APRENDIZ_CNPJ_INVALIDO`).
      allOf:
        - if:
            properties:
              modalidade:
                const: DIRETA
          then:
            required:
              - cnpj_entidade_qualificadora
            not:
              required:
                - estabelecimento_da_cota
        - if:
            properties:
              modalidade:
                const: INDIRETA
          then:
            required:
              - estabelecimento_da_cota
            not:
              required:
                - cnpj_entidade_qualificadora
    Dependente:
      type: object
      additionalProperties: false
      required:
        - tipo
        - nome
        - data_nascimento
      description: |
        Um dependente, como o S-2200 o declara. Não há sexo: o leiaute só o aceita
        no regime próprio de previdência, e o Motor declara o regime geral — o
        campo é recusado (`CAMPO_DESCONHECIDO`). Os três indicadores são só
        `true`, `false` ou ausentes (ausente = `false`); texto como "sim" é
        recusado, nunca convertido.

        Recusas, em `campos[]` (`dependentes.N.campo`), com o código do servidor:
        `DEPENDENTE_TIPO_OBRIGATORIO` · `DEPENDENTE_TIPO_INVALIDO` ·
        `DEPENDENTE_NOME_OBRIGATORIO` · `DEPENDENTE_NOME_INVALIDO` ·
        `DEPENDENTE_NASCIMENTO_INVALIDO` · `DEPENDENTE_CPF_OBRIGATORIO` ·
        `DEPENDENTE_CPF_INVALIDO` · `DEPENDENTE_CPF_DO_TRABALHADOR` ·
        `DEPENDENTE_CPF_DO_EMPREGADOR` · `DEPENDENTE_CPF_REPETIDO` ·
        `DEPENDENTE_REPETIDO` · `DEPENDENTE_DESCRICAO_OBRIGATORIA` ·
        `DEPENDENTE_DESCRICAO_INDEVIDA` · `DEPENDENTE_DESCRICAO_INVALIDA` ·
        `DEPENDENTE_LIMITE`.
      properties:
        tipo:
          type: string
          enum:
            - "01"
            - "02"
            - "03"
            - "06"
            - "09"
            - "10"
            - "11"
            - "12"
            - "99"
          description: |
            Código da Tabela 07 do eSocial (tipo de dependente), **obrigatório para
            todo dependente** — a biblioteca do eSocial o exige sempre, embora o
            XSD só o peça com IRRF (`DEPENDENTE_TIPO_OBRIGATORIO`). Só os
            vigentes: 04 e 07 foram encerrados em 25/04/2023, e 05 e 08 não
            existem (`DEPENDENTE_TIPO_INVALIDO`).
          x-enumDescriptions:
            "10": Menor pobre do qual detenha a guarda judicial
            "11": A pessoa absolutamente incapaz, da qual seja tutor ou curador
            "12": Ex-cônjuge
            "99": Agregado/Outros (exige `descricao`)
            "01": Cônjuge
            "02": Companheiro(a) com o(a) qual tenha filho ou viva há mais de 5 (cinco) anos ou possua declaração de união estável
            "03": Filho(a) ou enteado(a)
            "06": Irmão(ã), neto(a) ou bisneto(a) sem arrimo dos pais, do(a) qual detenha a guarda judicial
            "09": Pais, avós e bisavós
        nome:
          type: string
          minLength: 2
          maxLength: 70
          pattern: ^.*\S.*$
          description: Uma linha só, de 2 a 70 caracteres (`TS_nmDep`).
        data_nascimento:
          type: string
          format: date
          description: Não pode ser futura nem anterior a 1890 (`DEPENDENTE_NASCIMENTO_INVALIDO`).
        cpf:
          type: string
          pattern: ^\d{11}$
          description: |
            Obrigatório quando `dependente_irrf` = true (`DEPENDENTE_CPF_OBRIGATORIO`).
            Dígitos conferidos (`DEPENDENTE_CPF_INVALIDO`); não pode ser o do próprio
            trabalhador (`DEPENDENTE_CPF_DO_TRABALHADOR`) nem repetir o de outro
            dependente (`DEPENDENTE_CPF_REPETIDO`).
        dependente_irrf:
          type: boolean
          default: false
          description: |
            Dependente para dedução do Imposto de Renda. Os marcados são a
            contagem que a folha usa.
        dependente_salario_familia:
          type: boolean
          default: false
        incapaz_para_trabalho:
          type: boolean
          default: false
          description: Tem incapacidade física ou mental para o trabalho.
        descricao:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^\S[\s\S]*$
          description: |
            Obrigatória quando `tipo` = "99" (`DEPENDENTE_DESCRICAO_OBRIGATORIA`),
            e só aceita nesse caso (`DEPENDENTE_DESCRICAO_INDEVIDA`). O primeiro
            caractere não pode ser espaço (`TS_descrDep` = `TS_texto_100`).
      allOf:
        - if:
            properties:
              dependente_irrf:
                const: true
            required:
              - dependente_irrf
          then:
            required:
              - cpf
        - if:
            properties:
              tipo:
                const: "99"
            required:
              - tipo
          then:
            required:
              - descricao
          else:
            not:
              required:
                - descricao
    Estado:
      type: string
      enum:
        - RECEBIDA
        - AGUARDANDO_ESCRITORIO
        - ENVIADA
        - PROCESSADA
        - RECUSADA
        - DESCARTADA
      description: Onde a admissão está.
      x-enumDescriptions:
        RECEBIDA: Colaborador criado; o evento está sendo montado.
        AGUARDANDO_ESCRITORIO: O evento está na fila do escritório, esperando uma pessoa aprovar.
        ENVIADA: Transmitido; aguardando o processamento do eSocial.
        PROCESSADA: Aceito pelo eSocial; há `nr_recibo`.
        RECUSADA: Recusado pelo eSocial; veja `ocorrencias`. Se o escritório corrigir e reenviar, pode voltar a ENVIADA; não a trate como final.
        DESCARTADA: O escritório descartou o evento sem enviá-lo. Pode voltar se ele o restaurar.
    Ambiente:
      type: string
      enum:
        - PRODUCAO
        - RESTRITA
      description: Ambiente do eSocial da empresa.
      x-enumDescriptions:
        PRODUCAO: Produção (com valor legal).
        RESTRITA: Produção Restrita (teste, sem valor legal).
    AvisoDaAdmissao:
      type: object
      additionalProperties: false
      required:
        - codigo
        - mensagem
      properties:
        codigo:
          type: string
        mensagem:
          type: string
    AdmissaoAceita:
      type: object
      additionalProperties: false
      required:
        - id
        - estado
        - matricula
        - ambiente
        - prazo_legal
        - fora_do_prazo
        - recebida_em
        - avisos
      properties:
        id:
          type: string
          format: uuid
          description: Número de acompanhamento.
        estado:
          $ref: "#/components/schemas/Estado"
        matricula:
          type: string
          maxLength: 30
          description: |
            Gerada pelo Motor pelo modelo de matrícula da empresa. Definitiva: o
            eSocial a fixa no primeiro envio. Guarde-a junto do CPF.
        ambiente:
          $ref: "#/components/schemas/Ambiente"
        prazo_legal:
          type: string
          format: date
          description: Último dia para enviar o S-2200 (a véspera da admissão).
        fora_do_prazo:
          type: boolean
          description: |
            `true` quando a `data_admissao` é o dia do pedido ou anterior (horário
            de Brasília) — o prazo é a véspera, então nesse caso ele já passou.
            Não bloqueia o envio.
        recebida_em:
          type: string
          format: date-time
        avisos:
          type: array
          description: |
            Observações que não impediram a admissão, como `CBO_DO_CADASTRO`
            (cargo já existente com CBO diferente do informado — ver `Cargo`).
          items:
            $ref: "#/components/schemas/AvisoDaAdmissao"
    AdmissaoAndamento:
      type: object
      additionalProperties: false
      required:
        - id
        - estado
        - matricula
        - ambiente
        - prazo_legal
        - fora_do_prazo
        - recebida_em
        - atualizada_em
        - nr_recibo
        - ocorrencias
        - xml_evento
        - xml_retorno
      properties:
        id:
          type: string
          format: uuid
        estado:
          $ref: "#/components/schemas/Estado"
        matricula:
          type: string
          maxLength: 30
        ambiente:
          $ref: "#/components/schemas/Ambiente"
        prazo_legal:
          type: string
          format: date
        fora_do_prazo:
          type: boolean
        recebida_em:
          type: string
          format: date-time
        atualizada_em:
          type: string
          format: date-time
        nr_recibo:
          type:
            - string
            - "null"
          pattern: ^1\.\d\.\d{19}$
          description: Número do recibo do eSocial. Só em PROCESSADA.
        ocorrencias:
          type: array
          items:
            $ref: "#/components/schemas/Ocorrencia"
        xml_evento:
          type:
            - string
            - "null"
          description: XML do S-2200 assinado, exatamente como foi enviado. Nulo antes de ENVIADA.
        xml_retorno:
          type:
            - string
            - "null"
          description: XML do retorno do eSocial para ESTE evento (recortado do lote). Só em PROCESSADA ou RECUSADA.
    Ocorrencia:
      type: object
      additionalProperties: false
      required:
        - codigo
        - descricao
        - tipo
        - localizacao
      description: Ocorrência devolvida pelo eSocial, sem tradução.
      properties:
        codigo:
          type:
            - string
            - "null"
        descricao:
          type:
            - string
            - "null"
        tipo:
          type:
            - string
            - "null"
          description: Tipo da ocorrência no retorno do eSocial (erro ou advertência).
        localizacao:
          type:
            - string
            - "null"
    Jornada:
      type: object
      additionalProperties: false
      required:
        - id
        - nome
        - tp_jornada
        - qtd_hrs_sem
        - dsc_jorn
        - tmp_parc
        - hor_noturno
        - pronta_para_esocial
      properties:
        id:
          type: string
          format: uuid
        nome:
          type: string
        tp_jornada:
          type: integer
          enum:
            - 2
            - 3
            - 4
            - 5
            - 6
            - 7
            - 9
          description: Tipo de jornada do eSocial.
          x-enumDescriptions:
            "2": Jornada 12 x 36 (12 horas de trabalho seguidas de 36 horas ininterruptas de descanso)
            "3": Jornada com horário diário fixo e folga variável
            "4": Jornada com horário diário fixo e folga fixa (no domingo)
            "5": Jornada com horário diário fixo e folga fixa (exceto no domingo)
            "6": Jornada com horário diário fixo e folga fixa (em outro dia da semana), com folga adicional periódica no domingo
            "7": Turno ininterrupto de revezamento
            "9": Demais tipos de jornada
        qtd_hrs_sem:
          type:
            - number
            - "null"
        dsc_jorn:
          type:
            - string
            - "null"
        tmp_parc:
          type: integer
          enum:
            - 0
            - 1
            - 2
            - 3
          description: 1 só vale para empregado doméstico, fora da v1 — jornada com 1 recusa (`JORNADA_PARCIAL_SO_DOMESTICO`).
          x-enumDescriptions:
            "0": Não é contrato em tempo parcial
            "1": Limitado a 25 horas semanais
            "2": Limitado a 30 horas semanais
            "3": Limitado a 26 horas semanais
        hor_noturno:
          type: boolean
        pronta_para_esocial:
          type: boolean
          description: Tem horas semanais e descrição e não é parcial de doméstico — o que o S-2200 exige.
    Sindicato:
      type: object
      additionalProperties: false
      required:
        - id
        - nome
        - cnpj
        - dt_base
      properties:
        id:
          type: string
          format: uuid
        nome:
          type: string
        cnpj:
          type:
            - string
            - "null"
          pattern: ^\d{14}$
        dt_base:
          type:
            - integer
            - "null"
          minimum: 1
          maximum: 12
          description: Mês da data-base da categoria.
    Erro:
      type: object
      additionalProperties: false
      required:
        - codigo
        - mensagem
        - campos
      properties:
        codigo:
          type: string
          description: |
            Código estável. Conhecidos: IDEMPOTENCY_KEY_INVALIDA, CHAVE_INVALIDA,
            CHAVE_REVOGADA, CHAVE_SEM_ESCOPO, EMPRESA_NAO_HABILITADA,
            CERTIFICADO_INDISPONIVEL, NAO_ENCONTRADA, METODO_NAO_PERMITIDO,
            TRABALHADOR_JA_ADMITIDO, IDEMPOTENCIA_CONFLITO, IDEMPOTENCIA_EM_CURSO,
            CORPO_GRANDE_DEMAIS, CAMPO_DESCONHECIDO, DADOS_INVALIDOS,
            DADOS_FALTANDO, LIMITE_EXCEDIDO, INDISPONIVEL (a lista, com o que
            fazer em cada um, está em `x-motor-codigos.recusas`). Trate código
            desconhecido como erro que exige olhar humano.
        mensagem:
          type: string
          description: Texto em português para pessoas. Não o use para decidir — use `codigo`.
        campos:
          type: array
          description: Problemas campo a campo. Vazio quando o erro não é de um campo.
          items:
            $ref: "#/components/schemas/CampoRecusado"
    CampoRecusado:
      type: object
      additionalProperties: false
      required:
        - campo
        - codigo
        - mensagem
      properties:
        campo:
          type: string
          description: Caminho no corpo, com ponto e índice — `trabalhador.cpf`, `dependentes.0.cpf`. `(corpo)` quando o JSON não pôde ser lido.
        codigo:
          type: string
          description: |
            Código do campo; a lista completa, com quem corrige cada um, está em
            `x-motor-codigos.campos`.
            Quem corrige depende do código: os de jornada e sindicato são do
            cadastro do escritório, não do pedido.
        mensagem:
          type: string
    Aviso:
      type: object
      additionalProperties: false
      required:
        - id
        - evento
        - ocorrido_em
        - ambiente
        - admissao
      description: |
        Leva o recibo e as ocorrências, **nunca** o XML nem dados pessoais (CPF,
        salário, endereço): os XML vêm por `GET /v1/admissoes/{id}`, com a chave.
        Entrega pelo menos uma vez e sem ordem garantida — vale o mais recente
        por `ocorrido_em`.
      properties:
        id:
          type: string
          format: uuid
          description: Igual ao cabeçalho `X-Motor-Aviso-Id`.
        evento:
          type: string
          enum:
            - admissao.processada
            - admissao.recusada
            - admissao.descartada
            - admissao.aguardando_escritorio
          x-enumDescriptions:
            admissao.processada: O eSocial aceitou o S-2200.
            admissao.recusada: O eSocial recusou o S-2200.
            admissao.descartada: O escritório descartou o evento antes de enviá-lo.
            admissao.aguardando_escritorio: O envio automático parou e o evento está na fila do escritório.
        ocorrido_em:
          type: string
          format: date-time
          description: Quando a mudança de estado aconteceu no Motor (não quando o aviso foi entregue).
        ambiente:
          $ref: "#/components/schemas/Ambiente"
        admissao:
          type: object
          additionalProperties: false
          required:
            - id
            - estado
            - matricula
            - nr_recibo
            - ocorrencias
            - motivo
          properties:
            id:
              type: string
              format: uuid
            estado:
              $ref: "#/components/schemas/Estado"
            matricula:
              type: string
              maxLength: 30
            nr_recibo:
              type:
                - string
                - "null"
              pattern: ^1\.\d\.\d{19}$
            ocorrencias:
              type: array
              items:
                $ref: "#/components/schemas/Ocorrencia"
            motivo:
              type:
                - string
                - "null"
              description: Em `descartada`, o motivo escrito pelo escritório (opcional para ele); em `aguardando_escritorio`, por que o envio automático parou.
