# Certificados

Upload e gerenciamento de certificados digitais (e-CNPJ / e-CPF).

Certificados são necessários para emissão em homologação e produção. Para sandbox, gere um certificado self-signed localmente (veja instruções na seção Ambientes acima).

## Listar certificados

> Lista os certificados visíveis no contexto autenticado.\
> \
> Regras:\
> \- O parâmetro \`fields\` pode restringir a resposta a um subconjunto dos campos documentados no schema.\
> \- \`query\_mode\` é aceito por compatibilidade com outras listagens paginadas e o comportamento atual permanece paginado.\
> \- A listagem ordena certificados por \`expiration\_date\` crescente, deixando os vencimentos mais próximos primeiro.\
> \- Chave de workspace lista certificados do workspace.\
> \- Chave de empresa lista apenas certificados cuja raiz do CNPJ corresponde à empresa vinculada à chave.

```json
{"openapi":"3.1.1","info":{"title":"API IXC E-Docs - Integração x-api-key","version":"0.1.0"},"tags":[{"name":"Certificados","description":"Upload e gerenciamento de certificados digitais (e-CNPJ / e-CPF).\n\nCertificados são necessários para emissão em homologação e produção.\nPara sandbox, gere um certificado self-signed localmente (veja instruções na seção Ambientes acima)."}],"servers":[{"url":"https://sandbox.api.edocs.ixcsoft.com.br","description":"Sandbox — simulação interna, aceita certificado self-signed, sem comunicação com a prefeitura."},{"url":"https://api.edocs.ixcsoft.com.br","description":"Produção / Homologação — ambiente real, controlado por `nfse_config.environment`."}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Chave de API enviada no header `x-api-key`.\n\nExistem dois níveis de chave:\n- Workspace: permite emitir NFS-e para várias empresas do mesmo workspace e pode associar webhooks/integrações via `company_id` ou `tax_id`.\n- Empresa: permite emitir NFS-e e gerir certificados apenas para a raiz do CNPJ vinculado e autoassocia webhooks/integrações à própria empresa.\n- Se a integração tiver `allowed_ips`, o acesso só é liberado quando o IP resolvido por `request.ip` estiver dentro da allowlist configurada.\n\nPara gerar a chave de workspace, crie uma integração no painel\n(sem `company_id`) e utilize o `api_key` retornado."}},"parameters":{"AcceptLanguage":{"name":"Accept-Language","in":"header","required":false,"description":"Idioma preferencial para mensagens de erro (ex.: pt-BR).","schema":{"type":"string"}}},"schemas":{"CertificateListResponse":{"type":"object","additionalProperties":false,"required":["items","paginate"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CertificateResponse"}},"paginate":{"$ref":"#/components/schemas/CursorPaginateMeta"}}},"CertificateResponse":{"type":"object","additionalProperties":false,"required":["id","workspace_id","tax_id"],"properties":{"id":{"$ref":"#/components/schemas/ObjectId"},"workspace_id":{"$ref":"#/components/schemas/ObjectId"},"name":{"type":["string","null"]},"expiration_date":{"type":["string","null"],"format":"date-time"},"tax_id":{"type":["string","null"]},"hash":{"type":["string","null"]},"is_sandbox":{"type":"boolean","description":"Indica se o certificado é sandbox (self-signed gerado pela API)."},"file":{"oneOf":[{"$ref":"#/components/schemas/CertificateFileResponse"},{"type":"null"}]},"notifications":{"type":["array","null"],"items":{"$ref":"#/components/schemas/CertificateNotification"}},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}}},"ObjectId":{"type":"string","description":"Identificador no formato ObjectId."},"CertificateFileResponse":{"type":"object","additionalProperties":false,"properties":{"bucket":{"type":["string","null"]},"key":{"type":"string"},"name":{"type":["string","null"]},"region":{"type":["string","null"]},"size":{"type":["number","null"]},"uploaded_at":{"type":["string","null"],"format":"date-time"}}},"CertificateNotification":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email"}}},"CursorPaginateMeta":{"type":"object","additionalProperties":false,"required":["limit","has_next_page","has_previous_page","start_cursor","end_cursor"],"properties":{"limit":{"type":"integer","minimum":10,"maximum":100},"has_next_page":{"type":"boolean"},"has_previous_page":{"type":"boolean"},"start_cursor":{"type":["string","null"]},"end_cursor":{"type":["string","null"]}}},"ValidationErrorResponse":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"description":"Erro de validação (Zod ou validador de domínio). No `POST /nfse`, `error.code` é `NFSE_VALIDATION_FAILED`; `error.details[]` carrega cada campo inválido com códigos catalogados ou `VALIDATION_*` e `params.field`."},"ErrorResponse":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"object","additionalProperties":false,"required":["code","message","status"],"properties":{"code":{"type":"string","description":"Código machine-readable do erro. Detalhes em `docs/ERRORS.md`."},"message":{"type":"string","description":"Mensagem traduzida conforme Accept-Language."},"status":{"type":"integer","description":"HTTP status code."},"params":{"type":"object","description":"Dados estruturados do erro (campos referenciados, identificadores, contexto técnico). Reflete a interpolação `{{var}}` aplicada ao template da mensagem.","additionalProperties":true},"details":{"type":"array","description":"Lista de falhas estruturadas adicionais (presente quando há múltiplas falhas, ex.: validação Zod, múltiplas empresas inválidas).","items":{"$ref":"#/components/schemas/ErrorDetail"}}}}}},"ErrorDetail":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","description":"Código machine-readable do detalhe."},"message":{"type":"string","description":"Mensagem traduzida do detalhe conforme Accept-Language."},"params":{"type":"object","description":"Dados estruturados do detalhe (campo referenciado, identificadores, etc.).","additionalProperties":true}}}},"responses":{"UnauthorizedError":{"description":"Não autenticado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Não autorizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalServerError":{"description":"Erro interno inesperado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ServiceUnderMaintenance":{"description":"Sistema em manutenção programada","headers":{"Retry-After":{"description":"Data e hora prevista para o fim da manutenção no formato HTTP-date (RFC 7231).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/certificates":{"get":{"operationId":"listCertificates","tags":["Certificados"],"summary":"Listar certificados","description":"Lista os certificados visíveis no contexto autenticado.\n\nRegras:\n- O parâmetro `fields` pode restringir a resposta a um subconjunto dos campos documentados no schema.\n- `query_mode` é aceito por compatibilidade com outras listagens paginadas e o comportamento atual permanece paginado.\n- A listagem ordena certificados por `expiration_date` crescente, deixando os vencimentos mais próximos primeiro.\n- Chave de workspace lista certificados do workspace.\n- Chave de empresa lista apenas certificados cuja raiz do CNPJ corresponde à empresa vinculada à chave.","parameters":[{"$ref":"#/components/parameters/AcceptLanguage"},{"name":"q","in":"query","required":false,"description":"Busca textual por nome, CNPJ ou hash do certificado.","schema":{"type":"string"}},{"name":"expires_in_days","in":"query","required":false,"description":"Filtra certificados que vencem a partir de agora até a janela informada.","schema":{"type":"integer","enum":[1,10,30]}},{"name":"tax_id_root","in":"query","required":false,"description":"Filtra certificados pela raiz de oito dígitos do CNPJ, útil para localizar matriz e filiais do mesmo grupo.","schema":{"type":"string","minLength":8,"maxLength":8,"pattern":"^[0-9]{8}$"}},{"name":"query_mode","in":"query","required":false,"description":"Modo aceito pela API para esta listagem.","schema":{"type":"string","enum":["list","paginated"],"default":"paginated"}},{"name":"cursor","in":"query","required":false,"description":"Cursor opaco de paginação.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Quantidade de itens por página (10 a 100).","schema":{"type":"integer","minimum":10,"maximum":100,"default":10}},{"name":"fields","in":"query","required":false,"description":"Seleção de campos separados por vírgula.","schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de certificados","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificateListResponse"}}}},"400":{"description":"Erro de validação","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"500":{"$ref":"#/components/responses/InternalServerError"},"503":{"$ref":"#/components/responses/ServiceUnderMaintenance"}}}}}}
```

## Enviar certificado digital

> Envia um certificado digital para uso na emissão de NFS-e.\
> \
> Regras importantes:\
> \- Upload via \`multipart/form-data\`.\
> \- Campo do arquivo: \`file\`.\
> \- Extensões permitidas: .pfx, .p12, .cer, .p7, .p7b.\
> \- Tamanho máximo: 1 MB.\
> \- Chave de workspace pode cadastrar certificados do workspace.\
> \- Chave de empresa só pode cadastrar certificado cuja raiz do CNPJ corresponde à empresa vinculada à chave.\
> \- CNPJ extraído do certificado é normalizado para dígitos, inclusive quando vier mascarado no CN.\
> \- A comparação de escopo usa os 8 primeiros dígitos do CNPJ, permitindo matriz e filiais da mesma raiz.

```json
{"openapi":"3.1.1","info":{"title":"API IXC E-Docs - Integração x-api-key","version":"0.1.0"},"tags":[{"name":"Certificados","description":"Upload e gerenciamento de certificados digitais (e-CNPJ / e-CPF).\n\nCertificados são necessários para emissão em homologação e produção.\nPara sandbox, gere um certificado self-signed localmente (veja instruções na seção Ambientes acima)."}],"servers":[{"url":"https://sandbox.api.edocs.ixcsoft.com.br","description":"Sandbox — simulação interna, aceita certificado self-signed, sem comunicação com a prefeitura."},{"url":"https://api.edocs.ixcsoft.com.br","description":"Produção / Homologação — ambiente real, controlado por `nfse_config.environment`."}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Chave de API enviada no header `x-api-key`.\n\nExistem dois níveis de chave:\n- Workspace: permite emitir NFS-e para várias empresas do mesmo workspace e pode associar webhooks/integrações via `company_id` ou `tax_id`.\n- Empresa: permite emitir NFS-e e gerir certificados apenas para a raiz do CNPJ vinculado e autoassocia webhooks/integrações à própria empresa.\n- Se a integração tiver `allowed_ips`, o acesso só é liberado quando o IP resolvido por `request.ip` estiver dentro da allowlist configurada.\n\nPara gerar a chave de workspace, crie uma integração no painel\n(sem `company_id`) e utilize o `api_key` retornado."}},"parameters":{"AcceptLanguage":{"name":"Accept-Language","in":"header","required":false,"description":"Idioma preferencial para mensagens de erro (ex.: pt-BR).","schema":{"type":"string"}}},"schemas":{"CertificateCreateRequest":{"type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"Arquivo do certificado (.pfx, .p12, .cer, .p7, .p7b)."},"password":{"type":"string","maxLength":256,"description":"Senha do certificado (obrigatória para PFX/P12)."},"notifications":{"type":"array","maxItems":5,"items":{"$ref":"#/components/schemas/CertificateNotification"}}}},"CertificateNotification":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email"}}},"CertificateResponse":{"type":"object","additionalProperties":false,"required":["id","workspace_id","tax_id"],"properties":{"id":{"$ref":"#/components/schemas/ObjectId"},"workspace_id":{"$ref":"#/components/schemas/ObjectId"},"name":{"type":["string","null"]},"expiration_date":{"type":["string","null"],"format":"date-time"},"tax_id":{"type":["string","null"]},"hash":{"type":["string","null"]},"is_sandbox":{"type":"boolean","description":"Indica se o certificado é sandbox (self-signed gerado pela API)."},"file":{"oneOf":[{"$ref":"#/components/schemas/CertificateFileResponse"},{"type":"null"}]},"notifications":{"type":["array","null"],"items":{"$ref":"#/components/schemas/CertificateNotification"}},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}}},"ObjectId":{"type":"string","description":"Identificador no formato ObjectId."},"CertificateFileResponse":{"type":"object","additionalProperties":false,"properties":{"bucket":{"type":["string","null"]},"key":{"type":"string"},"name":{"type":["string","null"]},"region":{"type":["string","null"]},"size":{"type":["number","null"]},"uploaded_at":{"type":["string","null"],"format":"date-time"}}},"ErrorResponse":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"object","additionalProperties":false,"required":["code","message","status"],"properties":{"code":{"type":"string","description":"Código machine-readable do erro. Detalhes em `docs/ERRORS.md`."},"message":{"type":"string","description":"Mensagem traduzida conforme Accept-Language."},"status":{"type":"integer","description":"HTTP status code."},"params":{"type":"object","description":"Dados estruturados do erro (campos referenciados, identificadores, contexto técnico). Reflete a interpolação `{{var}}` aplicada ao template da mensagem.","additionalProperties":true},"details":{"type":"array","description":"Lista de falhas estruturadas adicionais (presente quando há múltiplas falhas, ex.: validação Zod, múltiplas empresas inválidas).","items":{"$ref":"#/components/schemas/ErrorDetail"}}}}}},"ErrorDetail":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","description":"Código machine-readable do detalhe."},"message":{"type":"string","description":"Mensagem traduzida do detalhe conforme Accept-Language."},"params":{"type":"object","description":"Dados estruturados do detalhe (campo referenciado, identificadores, etc.).","additionalProperties":true}}},"ValidationErrorResponse":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"description":"Erro de validação (Zod ou validador de domínio). No `POST /nfse`, `error.code` é `NFSE_VALIDATION_FAILED`; `error.details[]` carrega cada campo inválido com códigos catalogados ou `VALIDATION_*` e `params.field`."}},"responses":{"UnauthorizedError":{"description":"Não autenticado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalServerError":{"description":"Erro interno inesperado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ServiceUnderMaintenance":{"description":"Sistema em manutenção programada","headers":{"Retry-After":{"description":"Data e hora prevista para o fim da manutenção no formato HTTP-date (RFC 7231).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/certificates":{"post":{"operationId":"createCertificate","tags":["Certificados"],"summary":"Enviar certificado digital","description":"Envia um certificado digital para uso na emissão de NFS-e.\n\nRegras importantes:\n- Upload via `multipart/form-data`.\n- Campo do arquivo: `file`.\n- Extensões permitidas: .pfx, .p12, .cer, .p7, .p7b.\n- Tamanho máximo: 1 MB.\n- Chave de workspace pode cadastrar certificados do workspace.\n- Chave de empresa só pode cadastrar certificado cuja raiz do CNPJ corresponde à empresa vinculada à chave.\n- CNPJ extraído do certificado é normalizado para dígitos, inclusive quando vier mascarado no CN.\n- A comparação de escopo usa os 8 primeiros dígitos do CNPJ, permitindo matriz e filiais da mesma raiz.","parameters":[{"$ref":"#/components/parameters/AcceptLanguage"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/CertificateCreateRequest"},"encoding":{"file":{"contentType":"application/x-pkcs12, application/pkix-cert"}}}}},"responses":{"201":{"description":"Certificado criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificateResponse"}}}},"400":{"description":"Erro de validação, upload inválido ou certificado rejeitado","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/ValidationErrorResponse"}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"description":"Certificado fora da raiz de CNPJ da chave de empresa","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Certificado já cadastrado no workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"$ref":"#/components/responses/InternalServerError"},"503":{"$ref":"#/components/responses/ServiceUnderMaintenance"}}}}}}
```

## Resumo de vencimento de certificados

> Conta certificados ainda válidos que vencem dentro da janela informada.\
> \
> Regras:\
> \- No app, a mesma operação é consumida pela rota escopada do workspace: \`GET /workspaces/{workspace\_id}/certificates/expiration-summary\`.\
> \- A janela padrão é de 30 dias.\
> \- O resultado é uma contagem exata e não depende do limite da listagem paginada.\
> \- O backend cacheia o resumo por 24 horas no Valkey e invalida o cache quando certificados do workspace são criados, atualizados ou excluídos.\
> \- Chave de empresa conta apenas certificados cuja raiz do CNPJ corresponde à empresa vinculada à chave.

```json
{"openapi":"3.1.1","info":{"title":"API IXC E-Docs - Integração x-api-key","version":"0.1.0"},"tags":[{"name":"Certificados","description":"Upload e gerenciamento de certificados digitais (e-CNPJ / e-CPF).\n\nCertificados são necessários para emissão em homologação e produção.\nPara sandbox, gere um certificado self-signed localmente (veja instruções na seção Ambientes acima)."}],"servers":[{"url":"https://sandbox.api.edocs.ixcsoft.com.br","description":"Sandbox — simulação interna, aceita certificado self-signed, sem comunicação com a prefeitura."},{"url":"https://api.edocs.ixcsoft.com.br","description":"Produção / Homologação — ambiente real, controlado por `nfse_config.environment`."}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Chave de API enviada no header `x-api-key`.\n\nExistem dois níveis de chave:\n- Workspace: permite emitir NFS-e para várias empresas do mesmo workspace e pode associar webhooks/integrações via `company_id` ou `tax_id`.\n- Empresa: permite emitir NFS-e e gerir certificados apenas para a raiz do CNPJ vinculado e autoassocia webhooks/integrações à própria empresa.\n- Se a integração tiver `allowed_ips`, o acesso só é liberado quando o IP resolvido por `request.ip` estiver dentro da allowlist configurada.\n\nPara gerar a chave de workspace, crie uma integração no painel\n(sem `company_id`) e utilize o `api_key` retornado."}},"parameters":{"AcceptLanguage":{"name":"Accept-Language","in":"header","required":false,"description":"Idioma preferencial para mensagens de erro (ex.: pt-BR).","schema":{"type":"string"}}},"schemas":{"CertificateExpirationSummaryResponse":{"type":"object","additionalProperties":false,"required":["expiring_count"],"properties":{"expiring_count":{"type":"integer","minimum":0,"description":"Quantidade de certificados ainda válidos que vencem dentro da janela solicitada."}}},"ValidationErrorResponse":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"description":"Erro de validação (Zod ou validador de domínio). No `POST /nfse`, `error.code` é `NFSE_VALIDATION_FAILED`; `error.details[]` carrega cada campo inválido com códigos catalogados ou `VALIDATION_*` e `params.field`."},"ErrorResponse":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"object","additionalProperties":false,"required":["code","message","status"],"properties":{"code":{"type":"string","description":"Código machine-readable do erro. Detalhes em `docs/ERRORS.md`."},"message":{"type":"string","description":"Mensagem traduzida conforme Accept-Language."},"status":{"type":"integer","description":"HTTP status code."},"params":{"type":"object","description":"Dados estruturados do erro (campos referenciados, identificadores, contexto técnico). Reflete a interpolação `{{var}}` aplicada ao template da mensagem.","additionalProperties":true},"details":{"type":"array","description":"Lista de falhas estruturadas adicionais (presente quando há múltiplas falhas, ex.: validação Zod, múltiplas empresas inválidas).","items":{"$ref":"#/components/schemas/ErrorDetail"}}}}}},"ErrorDetail":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","description":"Código machine-readable do detalhe."},"message":{"type":"string","description":"Mensagem traduzida do detalhe conforme Accept-Language."},"params":{"type":"object","description":"Dados estruturados do detalhe (campo referenciado, identificadores, etc.).","additionalProperties":true}}}},"responses":{"UnauthorizedError":{"description":"Não autenticado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Não autorizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalServerError":{"description":"Erro interno inesperado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ServiceUnderMaintenance":{"description":"Sistema em manutenção programada","headers":{"Retry-After":{"description":"Data e hora prevista para o fim da manutenção no formato HTTP-date (RFC 7231).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/certificates/expiration-summary":{"get":{"operationId":"readCertificateExpirationSummary","tags":["Certificados"],"summary":"Resumo de vencimento de certificados","description":"Conta certificados ainda válidos que vencem dentro da janela informada.\n\nRegras:\n- No app, a mesma operação é consumida pela rota escopada do workspace: `GET /workspaces/{workspace_id}/certificates/expiration-summary`.\n- A janela padrão é de 30 dias.\n- O resultado é uma contagem exata e não depende do limite da listagem paginada.\n- O backend cacheia o resumo por 24 horas no Valkey e invalida o cache quando certificados do workspace são criados, atualizados ou excluídos.\n- Chave de empresa conta apenas certificados cuja raiz do CNPJ corresponde à empresa vinculada à chave.","parameters":[{"$ref":"#/components/parameters/AcceptLanguage"},{"name":"expires_in_days","in":"query","required":false,"description":"Janela de vencimento considerada na contagem.","schema":{"type":"integer","enum":[1,10,30],"default":30}}],"responses":{"200":{"description":"Resumo de certificados próximos do vencimento","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificateExpirationSummaryResponse"}}}},"400":{"description":"Erro de validação","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"500":{"$ref":"#/components/responses/InternalServerError"},"503":{"$ref":"#/components/responses/ServiceUnderMaintenance"}}}}}}
```

## Ler certificado

> Retorna os dados do certificado identificado na rota.\
> \
> Regras:\
> \- Chave de workspace pode ler certificados do workspace.\
> \- Chave de empresa só encontra certificados cuja raiz do CNPJ corresponde à empresa vinculada à chave.

```json
{"openapi":"3.1.1","info":{"title":"API IXC E-Docs - Integração x-api-key","version":"0.1.0"},"tags":[{"name":"Certificados","description":"Upload e gerenciamento de certificados digitais (e-CNPJ / e-CPF).\n\nCertificados são necessários para emissão em homologação e produção.\nPara sandbox, gere um certificado self-signed localmente (veja instruções na seção Ambientes acima)."}],"servers":[{"url":"https://sandbox.api.edocs.ixcsoft.com.br","description":"Sandbox — simulação interna, aceita certificado self-signed, sem comunicação com a prefeitura."},{"url":"https://api.edocs.ixcsoft.com.br","description":"Produção / Homologação — ambiente real, controlado por `nfse_config.environment`."}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Chave de API enviada no header `x-api-key`.\n\nExistem dois níveis de chave:\n- Workspace: permite emitir NFS-e para várias empresas do mesmo workspace e pode associar webhooks/integrações via `company_id` ou `tax_id`.\n- Empresa: permite emitir NFS-e e gerir certificados apenas para a raiz do CNPJ vinculado e autoassocia webhooks/integrações à própria empresa.\n- Se a integração tiver `allowed_ips`, o acesso só é liberado quando o IP resolvido por `request.ip` estiver dentro da allowlist configurada.\n\nPara gerar a chave de workspace, crie uma integração no painel\n(sem `company_id`) e utilize o `api_key` retornado."}},"parameters":{"CertificateId":{"name":"certificate_id","in":"path","required":true,"description":"ID do certificado no workspace.","schema":{"$ref":"#/components/schemas/ObjectId"}},"AcceptLanguage":{"name":"Accept-Language","in":"header","required":false,"description":"Idioma preferencial para mensagens de erro (ex.: pt-BR).","schema":{"type":"string"}}},"schemas":{"ObjectId":{"type":"string","description":"Identificador no formato ObjectId."},"CertificateResponse":{"type":"object","additionalProperties":false,"required":["id","workspace_id","tax_id"],"properties":{"id":{"$ref":"#/components/schemas/ObjectId"},"workspace_id":{"$ref":"#/components/schemas/ObjectId"},"name":{"type":["string","null"]},"expiration_date":{"type":["string","null"],"format":"date-time"},"tax_id":{"type":["string","null"]},"hash":{"type":["string","null"]},"is_sandbox":{"type":"boolean","description":"Indica se o certificado é sandbox (self-signed gerado pela API)."},"file":{"oneOf":[{"$ref":"#/components/schemas/CertificateFileResponse"},{"type":"null"}]},"notifications":{"type":["array","null"],"items":{"$ref":"#/components/schemas/CertificateNotification"}},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}}},"CertificateFileResponse":{"type":"object","additionalProperties":false,"properties":{"bucket":{"type":["string","null"]},"key":{"type":"string"},"name":{"type":["string","null"]},"region":{"type":["string","null"]},"size":{"type":["number","null"]},"uploaded_at":{"type":["string","null"],"format":"date-time"}}},"CertificateNotification":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email"}}},"ErrorResponse":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"object","additionalProperties":false,"required":["code","message","status"],"properties":{"code":{"type":"string","description":"Código machine-readable do erro. Detalhes em `docs/ERRORS.md`."},"message":{"type":"string","description":"Mensagem traduzida conforme Accept-Language."},"status":{"type":"integer","description":"HTTP status code."},"params":{"type":"object","description":"Dados estruturados do erro (campos referenciados, identificadores, contexto técnico). Reflete a interpolação `{{var}}` aplicada ao template da mensagem.","additionalProperties":true},"details":{"type":"array","description":"Lista de falhas estruturadas adicionais (presente quando há múltiplas falhas, ex.: validação Zod, múltiplas empresas inválidas).","items":{"$ref":"#/components/schemas/ErrorDetail"}}}}}},"ErrorDetail":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","description":"Código machine-readable do detalhe."},"message":{"type":"string","description":"Mensagem traduzida do detalhe conforme Accept-Language."},"params":{"type":"object","description":"Dados estruturados do detalhe (campo referenciado, identificadores, etc.).","additionalProperties":true}}}},"responses":{"UnauthorizedError":{"description":"Não autenticado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Não autorizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalServerError":{"description":"Erro interno inesperado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ServiceUnderMaintenance":{"description":"Sistema em manutenção programada","headers":{"Retry-After":{"description":"Data e hora prevista para o fim da manutenção no formato HTTP-date (RFC 7231).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/certificates/{certificate_id}":{"get":{"operationId":"getCertificate","tags":["Certificados"],"summary":"Ler certificado","description":"Retorna os dados do certificado identificado na rota.\n\nRegras:\n- Chave de workspace pode ler certificados do workspace.\n- Chave de empresa só encontra certificados cuja raiz do CNPJ corresponde à empresa vinculada à chave.","parameters":[{"$ref":"#/components/parameters/CertificateId"},{"$ref":"#/components/parameters/AcceptLanguage"}],"responses":{"200":{"description":"Certificado encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificateResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"description":"Certificado não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"$ref":"#/components/responses/InternalServerError"},"503":{"$ref":"#/components/responses/ServiceUnderMaintenance"}}}}}}
```

## Atualizar certificado

> Atualiza os metadados do certificado e aceita um novo arquivo opcionalmente.\
> \
> Regras importantes:\
> \- Upload via \`multipart/form-data\`.\
> \- Campo do arquivo: \`file\`.\
> \- Quando \`file\` não é enviado, a atualização pode alterar apenas senha e notificações.\
> \- A resposta preserva \`id\` com o mesmo valor de \`certificate\_id\` informado na rota.\
> \- Chave de workspace pode atualizar certificados do workspace.\
> \- Chave de empresa só pode atualizar certificado cuja raiz do CNPJ corresponde à empresa vinculada à chave.\
> \- Quando a chave é de empresa e um novo arquivo é enviado, a raiz do CNPJ do novo certificado também precisa corresponder à empresa.\
> \- CNPJ extraído do certificado é normalizado para dígitos, inclusive quando vier mascarado no CN.\
> \- A comparação de escopo usa os 8 primeiros dígitos do CNPJ, permitindo matriz e filiais da mesma raiz.

```json
{"openapi":"3.1.1","info":{"title":"API IXC E-Docs - Integração x-api-key","version":"0.1.0"},"tags":[{"name":"Certificados","description":"Upload e gerenciamento de certificados digitais (e-CNPJ / e-CPF).\n\nCertificados são necessários para emissão em homologação e produção.\nPara sandbox, gere um certificado self-signed localmente (veja instruções na seção Ambientes acima)."}],"servers":[{"url":"https://sandbox.api.edocs.ixcsoft.com.br","description":"Sandbox — simulação interna, aceita certificado self-signed, sem comunicação com a prefeitura."},{"url":"https://api.edocs.ixcsoft.com.br","description":"Produção / Homologação — ambiente real, controlado por `nfse_config.environment`."}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Chave de API enviada no header `x-api-key`.\n\nExistem dois níveis de chave:\n- Workspace: permite emitir NFS-e para várias empresas do mesmo workspace e pode associar webhooks/integrações via `company_id` ou `tax_id`.\n- Empresa: permite emitir NFS-e e gerir certificados apenas para a raiz do CNPJ vinculado e autoassocia webhooks/integrações à própria empresa.\n- Se a integração tiver `allowed_ips`, o acesso só é liberado quando o IP resolvido por `request.ip` estiver dentro da allowlist configurada.\n\nPara gerar a chave de workspace, crie uma integração no painel\n(sem `company_id`) e utilize o `api_key` retornado."}},"parameters":{"CertificateId":{"name":"certificate_id","in":"path","required":true,"description":"ID do certificado no workspace.","schema":{"$ref":"#/components/schemas/ObjectId"}},"AcceptLanguage":{"name":"Accept-Language","in":"header","required":false,"description":"Idioma preferencial para mensagens de erro (ex.: pt-BR).","schema":{"type":"string"}}},"schemas":{"ObjectId":{"type":"string","description":"Identificador no formato ObjectId."},"CertificateUpdateRequest":{"type":"object","additionalProperties":false,"properties":{"file":{"type":"string","format":"binary","description":"Arquivo opcional do certificado (.pfx, .p12, .cer, .p7, .p7b)."},"password":{"type":"string","maxLength":256,"description":"Senha do certificado (obrigatória quando um novo PFX/P12 exigir abertura)."},"notifications":{"type":"array","maxItems":5,"items":{"$ref":"#/components/schemas/CertificateNotification"}}}},"CertificateNotification":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email"}}},"CertificateResponse":{"type":"object","additionalProperties":false,"required":["id","workspace_id","tax_id"],"properties":{"id":{"$ref":"#/components/schemas/ObjectId"},"workspace_id":{"$ref":"#/components/schemas/ObjectId"},"name":{"type":["string","null"]},"expiration_date":{"type":["string","null"],"format":"date-time"},"tax_id":{"type":["string","null"]},"hash":{"type":["string","null"]},"is_sandbox":{"type":"boolean","description":"Indica se o certificado é sandbox (self-signed gerado pela API)."},"file":{"oneOf":[{"$ref":"#/components/schemas/CertificateFileResponse"},{"type":"null"}]},"notifications":{"type":["array","null"],"items":{"$ref":"#/components/schemas/CertificateNotification"}},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}}},"CertificateFileResponse":{"type":"object","additionalProperties":false,"properties":{"bucket":{"type":["string","null"]},"key":{"type":"string"},"name":{"type":["string","null"]},"region":{"type":["string","null"]},"size":{"type":["number","null"]},"uploaded_at":{"type":["string","null"],"format":"date-time"}}},"ErrorResponse":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"object","additionalProperties":false,"required":["code","message","status"],"properties":{"code":{"type":"string","description":"Código machine-readable do erro. Detalhes em `docs/ERRORS.md`."},"message":{"type":"string","description":"Mensagem traduzida conforme Accept-Language."},"status":{"type":"integer","description":"HTTP status code."},"params":{"type":"object","description":"Dados estruturados do erro (campos referenciados, identificadores, contexto técnico). Reflete a interpolação `{{var}}` aplicada ao template da mensagem.","additionalProperties":true},"details":{"type":"array","description":"Lista de falhas estruturadas adicionais (presente quando há múltiplas falhas, ex.: validação Zod, múltiplas empresas inválidas).","items":{"$ref":"#/components/schemas/ErrorDetail"}}}}}},"ErrorDetail":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","description":"Código machine-readable do detalhe."},"message":{"type":"string","description":"Mensagem traduzida do detalhe conforme Accept-Language."},"params":{"type":"object","description":"Dados estruturados do detalhe (campo referenciado, identificadores, etc.).","additionalProperties":true}}},"ValidationErrorResponse":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"}],"description":"Erro de validação (Zod ou validador de domínio). No `POST /nfse`, `error.code` é `NFSE_VALIDATION_FAILED`; `error.details[]` carrega cada campo inválido com códigos catalogados ou `VALIDATION_*` e `params.field`."}},"responses":{"UnauthorizedError":{"description":"Não autenticado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalServerError":{"description":"Erro interno inesperado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ServiceUnderMaintenance":{"description":"Sistema em manutenção programada","headers":{"Retry-After":{"description":"Data e hora prevista para o fim da manutenção no formato HTTP-date (RFC 7231).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/certificates/{certificate_id}":{"put":{"operationId":"updateCertificate","tags":["Certificados"],"summary":"Atualizar certificado","description":"Atualiza os metadados do certificado e aceita um novo arquivo opcionalmente.\n\nRegras importantes:\n- Upload via `multipart/form-data`.\n- Campo do arquivo: `file`.\n- Quando `file` não é enviado, a atualização pode alterar apenas senha e notificações.\n- A resposta preserva `id` com o mesmo valor de `certificate_id` informado na rota.\n- Chave de workspace pode atualizar certificados do workspace.\n- Chave de empresa só pode atualizar certificado cuja raiz do CNPJ corresponde à empresa vinculada à chave.\n- Quando a chave é de empresa e um novo arquivo é enviado, a raiz do CNPJ do novo certificado também precisa corresponder à empresa.\n- CNPJ extraído do certificado é normalizado para dígitos, inclusive quando vier mascarado no CN.\n- A comparação de escopo usa os 8 primeiros dígitos do CNPJ, permitindo matriz e filiais da mesma raiz.","parameters":[{"$ref":"#/components/parameters/CertificateId"},{"$ref":"#/components/parameters/AcceptLanguage"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/CertificateUpdateRequest"},"encoding":{"file":{"contentType":"application/x-pkcs12, application/pkix-cert"}}}}},"responses":{"200":{"description":"Certificado atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificateResponse"}}}},"400":{"description":"Erro de validação, upload inválido ou certificado rejeitado","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/ValidationErrorResponse"}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"description":"Certificado fora da raiz de CNPJ da chave de empresa","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Certificado não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Certificado já cadastrado no workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"$ref":"#/components/responses/InternalServerError"},"503":{"$ref":"#/components/responses/ServiceUnderMaintenance"}}}}}}
```

## Excluir certificado

> Exclui o certificado quando ele não está em uso por nenhuma empresa.\
> \
> Regras:\
> \- Chave de workspace pode excluir certificados do workspace.\
> \- Chave de empresa só pode excluir certificado cuja raiz do CNPJ corresponde à empresa vinculada à chave.

```json
{"openapi":"3.1.1","info":{"title":"API IXC E-Docs - Integração x-api-key","version":"0.1.0"},"tags":[{"name":"Certificados","description":"Upload e gerenciamento de certificados digitais (e-CNPJ / e-CPF).\n\nCertificados são necessários para emissão em homologação e produção.\nPara sandbox, gere um certificado self-signed localmente (veja instruções na seção Ambientes acima)."}],"servers":[{"url":"https://sandbox.api.edocs.ixcsoft.com.br","description":"Sandbox — simulação interna, aceita certificado self-signed, sem comunicação com a prefeitura."},{"url":"https://api.edocs.ixcsoft.com.br","description":"Produção / Homologação — ambiente real, controlado por `nfse_config.environment`."}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Chave de API enviada no header `x-api-key`.\n\nExistem dois níveis de chave:\n- Workspace: permite emitir NFS-e para várias empresas do mesmo workspace e pode associar webhooks/integrações via `company_id` ou `tax_id`.\n- Empresa: permite emitir NFS-e e gerir certificados apenas para a raiz do CNPJ vinculado e autoassocia webhooks/integrações à própria empresa.\n- Se a integração tiver `allowed_ips`, o acesso só é liberado quando o IP resolvido por `request.ip` estiver dentro da allowlist configurada.\n\nPara gerar a chave de workspace, crie uma integração no painel\n(sem `company_id`) e utilize o `api_key` retornado."}},"parameters":{"CertificateId":{"name":"certificate_id","in":"path","required":true,"description":"ID do certificado no workspace.","schema":{"$ref":"#/components/schemas/ObjectId"}},"AcceptLanguage":{"name":"Accept-Language","in":"header","required":false,"description":"Idioma preferencial para mensagens de erro (ex.: pt-BR).","schema":{"type":"string"}}},"schemas":{"ObjectId":{"type":"string","description":"Identificador no formato ObjectId."},"ErrorResponse":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"object","additionalProperties":false,"required":["code","message","status"],"properties":{"code":{"type":"string","description":"Código machine-readable do erro. Detalhes em `docs/ERRORS.md`."},"message":{"type":"string","description":"Mensagem traduzida conforme Accept-Language."},"status":{"type":"integer","description":"HTTP status code."},"params":{"type":"object","description":"Dados estruturados do erro (campos referenciados, identificadores, contexto técnico). Reflete a interpolação `{{var}}` aplicada ao template da mensagem.","additionalProperties":true},"details":{"type":"array","description":"Lista de falhas estruturadas adicionais (presente quando há múltiplas falhas, ex.: validação Zod, múltiplas empresas inválidas).","items":{"$ref":"#/components/schemas/ErrorDetail"}}}}}},"ErrorDetail":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","description":"Código machine-readable do detalhe."},"message":{"type":"string","description":"Mensagem traduzida do detalhe conforme Accept-Language."},"params":{"type":"object","description":"Dados estruturados do detalhe (campo referenciado, identificadores, etc.).","additionalProperties":true}}}},"responses":{"UnauthorizedError":{"description":"Não autenticado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ForbiddenError":{"description":"Não autorizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InternalServerError":{"description":"Erro interno inesperado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ServiceUnderMaintenance":{"description":"Sistema em manutenção programada","headers":{"Retry-After":{"description":"Data e hora prevista para o fim da manutenção no formato HTTP-date (RFC 7231).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/certificates/{certificate_id}":{"delete":{"operationId":"deleteCertificate","tags":["Certificados"],"summary":"Excluir certificado","description":"Exclui o certificado quando ele não está em uso por nenhuma empresa.\n\nRegras:\n- Chave de workspace pode excluir certificados do workspace.\n- Chave de empresa só pode excluir certificado cuja raiz do CNPJ corresponde à empresa vinculada à chave.","parameters":[{"$ref":"#/components/parameters/CertificateId"},{"$ref":"#/components/parameters/AcceptLanguage"}],"responses":{"204":{"description":"Certificado excluído"},"400":{"description":"Certificado em uso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"description":"Certificado não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"$ref":"#/components/responses/InternalServerError"},"503":{"$ref":"#/components/responses/ServiceUnderMaintenance"}}}}}}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ixc-soft.gitbook.io/e-docs/certificados.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
