{ "info": { "name": "Comprobify API", "description": "Electronic invoice API for Ecuador's SRI. Set `base_url`, `api_key`, and `admin_secret` in the collection variables before running requests. After creating an invoice, copy the returned `accessKey` into the `access_key` variable to use it across subsequent requests.\n\nDocument endpoints require both `Authorization: Bearer ` and `X-Issuer-Id: `. Tenant and issuer management require only the Bearer token. Admin endpoints use the `admin_secret` as the Bearer token.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ { "key": "base_url", "value": "https://your-api-url", "description": "Base URL of the API (no trailing slash)", "type": "string" }, { "key": "api_key", "value": "", "description": "Bearer API key issued for the tenant (from Register or POST /v1/keys)", "type": "string" }, { "key": "admin_secret", "value": "", "description": "Admin secret (ADMIN_SECRET env var on the server)", "type": "string" }, { "key": "access_key", "value": "", "description": "49-digit SRI access key — copy from Create Invoice response", "type": "string" }, { "key": "issuer_id", "value": "", "description": "Issuer ID — copy from List Issuers or Create Branch response. Used as X-Issuer-Id header on document requests.", "type": "string" }, { "key": "api_key_id", "value": "", "description": "API key ID — copy from List Keys response (used for revocation)", "type": "string" }, { "key": "tenant_id", "value": "", "description": "Tenant ID — copy from Create Tenant (admin) response", "type": "string" }, { "key": "notification_id", "value": "", "description": "Notification ID — copy from List Notifications response (used for Mark as Read)", "type": "string" }, { "key": "webhook_id", "value": "", "description": "Webhook endpoint ID — copy from Register Webhook response", "type": "string" } ], "item": [ { "name": "Registration", "description": "Public registration and email verification endpoints. No authentication required.", "item": [ { "name": "Register", "request": { "method": "POST", "header": [], "url": { "raw": "{{base_url}}/v1/register", "host": [ "{{base_url}}" ], "path": [ "v1", "register" ] }, "body": { "mode": "formdata", "formdata": [ { "key": "email", "value": "your@email.com", "type": "text" }, { "key": "ruc", "value": "1712345678001", "type": "text" }, { "key": "businessName", "value": "My Company S.A.", "type": "text" }, { "key": "tradeName", "value": "", "description": "Optional trade name", "type": "text", "disabled": true }, { "key": "mainAddress", "value": "", "description": "Optional main (fiscal) address", "type": "text", "disabled": true }, { "key": "branchCode", "value": "001", "type": "text" }, { "key": "issuePointCode", "value": "001", "type": "text" }, { "key": "emissionType", "value": "1", "type": "text" }, { "key": "requiredAccounting", "value": "false", "type": "text" }, { "key": "certPassword", "value": "", "type": "text" }, { "key": "cert", "type": "file", "src": "" }, { "key": "environment", "value": "1", "description": "Deprecated — ignored. All new accounts start in sandbox. Use POST /v1/tenants/promote to move to production.", "type": "text", "disabled": true }, { "key": "specialTaxpayer", "value": "", "description": "Optional special taxpayer resolution number", "type": "text", "disabled": true }, { "key": "branchAddress", "value": "", "description": "Optional branch address", "type": "text", "disabled": true }, { "key": "documentTypes", "value": "[\"01\"]", "description": "Optional. Document type codes to enable (default: [\"01\"])", "type": "text", "disabled": true }, { "key": "language", "value": "es", "description": "Optional. Email language: es (default) or en.", "type": "text", "disabled": true }, { "key": "verificationRedirectUrl", "value": "", "description": "Optional. Frontend URL for the email verification link. Token appended as ?token=. https required in production.", "type": "text", "disabled": true } ] }, "description": "Self-service registration. Creates a tenant, issuer, and sandbox API key in one call.\n\nStore the returned `apiKey` — it is shown only once. Copy it into the `api_key` collection variable.\n\nIf the email already exists and is not suspended, the endpoint revokes the current sandbox key and returns a new one (200 with `recovered: true`).\n\nOptional `verificationRedirectUrl` sets a frontend page for the email verification link." } }, { "name": "Verify Email", "request": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/v1/verify-email?token={{verification_token}}", "host": [ "{{base_url}}" ], "path": [ "v1", "verify-email" ], "query": [ { "key": "token", "value": "{{verification_token}}" } ] }, "description": "Verify your email address using the token from the registration email. Required before promoting to production." } }, { "name": "Resend Verification Email", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/resend-verification", "host": [ "{{base_url}}" ], "path": [ "v1", "resend-verification" ] }, "body": { "mode": "raw", "raw": "{\n \"email\": \"your@email.com\"\n}", "options": { "raw": { "language": "json" } } }, "description": "Resends the verification email. Generates a fresh token (invalidating the previous one) and resets the expiry.\n\nTwo rate limits apply:\n- IP-based: shared with POST /v1/register — 5 requests per hour per IP.\n- Per-account cooldown: 60 seconds between resends for the same email. Returns 429 if not elapsed.\n\nReturns the same generic message whether or not the email exists (no enumeration)." } } ] }, { "name": "Tenants", "description": "Tenant settings and lifecycle management. Require a Bearer API key.", "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{api_key}}", "type": "string" } ] }, "item": [ { "name": "Update Preferred Language", "request": { "method": "PATCH", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/tenants/language", "host": [ "{{base_url}}" ], "path": [ "v1", "tenants", "language" ] }, "body": { "mode": "raw", "raw": "{\n \"language\": \"en\"\n}", "options": { "raw": { "language": "json" } } }, "description": "Updates the preferred language for the authenticated tenant. Used for all outgoing emails. Supported values: `es` (Spanish, default), `en` (English)." } }, { "name": "Promote to Production", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/tenants/promote", "host": [ "{{base_url}}" ], "path": [ "v1", "tenants", "promote" ] }, "body": { "mode": "raw", "raw": "{\n \"initialSequentials\": [\n { \"issuerId\": 1, \"documentType\": \"01\", \"sequential\": 1 }\n ]\n}", "options": { "raw": { "language": "json" } } }, "description": "Promotes the entire tenant (all branches) from sandbox to production. This is **one-way** — cannot be reversed.\n\nAll active sandbox API keys are revoked automatically. The response includes one new production key per revoked sandbox key (same label). **Store all tokens immediately — they are shown only once.**\n\n`initialSequentials` is optional. If omitted, all active document types on all issuers start at sequential 1. Pass `issuerId` + `documentType` + `sequential` for each combination you want to pre-seed.\n\nRequires the tenant's email to be verified (status ACTIVE)." } } ] }, { "name": "API Keys", "description": "Tenant-facing API key management. Mint named keys for each integration, list them, and revoke leaked or unused ones. Require a Bearer API key.", "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{api_key}}", "type": "string" } ] }, "item": [ { "name": "List Keys", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/keys", "host": [ "{{base_url}}" ], "path": [ "v1", "keys" ] }, "description": "Returns every active key for the tenant. The plaintext token is **never** returned — only labels, environments, ids, and creation dates.\n\nCopy a key's `id` into the `api_key_id` variable to use it with Revoke Key." } }, { "name": "Mint Key", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/keys", "host": [ "{{base_url}}" ], "path": [ "v1", "keys" ] }, "body": { "mode": "raw", "raw": "{\n \"label\": \"erp-integration\",\n \"environment\": \"sandbox\"\n}", "options": { "raw": { "language": "json" } } }, "description": "Creates a new tenant-scoped API key. The plaintext token is shown **once** in the response — store it immediately.\n\n`label` (optional, max 100 chars) — a human-readable name for the integration, e.g. `frontend-prod`, `erp`, `mobile-app`.\n\n`environment` — `\"sandbox\"` (default) or `\"production\"`. Production keys can only be minted after the tenant has been promoted to production.\n\nWhen used on a document request, the key's environment must match the targeted issuer's environment — otherwise the request is rejected with 401." } }, { "name": "Revoke Key", "request": { "method": "DELETE", "url": { "raw": "{{base_url}}/v1/keys/{{api_key_id}}", "host": [ "{{base_url}}" ], "path": [ "v1", "keys", "{{api_key_id}}" ] }, "description": "Marks the key as inactive. The key stops working immediately and cannot be used to authenticate any future request.\n\nYou cannot revoke the key you are currently using to make this request — use a different key, or contact admin support." } } ] }, { "name": "Issuers", "description": "Issuer (branch / issue point) management. Require a Bearer API key.", "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{api_key}}", "type": "string" } ] }, "item": [ { "name": "List Issuers", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/issuers", "host": [ "{{base_url}}" ], "path": [ "v1", "issuers" ] }, "description": "Returns all active issuers (branches / issue points) belonging to the authenticated tenant.\n\nCopy an issuer's `id` into the `issuer_id` collection variable to use it as `X-Issuer-Id` on document requests." } }, { "name": "Get Issuer", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/issuers/{{issuer_id}}", "host": [ "{{base_url}}" ], "path": [ "v1", "issuers", "{{issuer_id}}" ] }, "description": "Returns profile information for a single issuer owned by the authenticated tenant: RUC, business name, trade name, branch/issue point codes, and certificate expiry/fingerprint." } }, { "name": "Create Branch", "request": { "method": "POST", "url": { "raw": "{{base_url}}/v1/issuers", "host": [ "{{base_url}}" ], "path": [ "v1", "issuers" ] }, "body": { "mode": "formdata", "formdata": [ { "key": "branchCode", "value": "002", "type": "text" }, { "key": "issuePointCode", "value": "001", "type": "text" }, { "key": "branchAddress", "value": "Av. 6 de Diciembre 123", "type": "text", "disabled": true }, { "key": "documentTypes", "value": "[\"01\"]", "description": "Optional. Document type codes to enable (default: [\"01\"])", "type": "text", "disabled": true }, { "key": "sourceIssuerId", "value": "{{issuer_id}}", "description": "Optional. Numeric id of the issuer to inherit cert/profile from. Defaults to the tenant's first existing issuer. Ignored if a cert file is uploaded.", "type": "text", "disabled": true }, { "key": "cert", "type": "file", "src": "", "disabled": true, "description": "Optional — leave blank to reuse the calling issuer certificate" }, { "key": "certPassword", "value": "", "type": "text", "disabled": true } ] }, "description": "Creates a new branch or issue point for the tenant. Inherits RUC, business name, and certificate from an existing issuer of the tenant.\n\n**No new API key is minted** — your existing tenant key already covers every branch via the `X-Issuer-Id` header. New branches inherit the tenant's current environment (sandbox or production).\n\nThe cert file and certPassword fields are optional — only provide them if the new branch needs a different certificate.\n\nTier limits apply: FREE allows 1 branch / 1 issue point, STARTER 3 / 2, GROWTH 10 / 5, BUSINESS unlimited." } }, { "name": "List Document Types", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/issuers/{{issuer_id}}/document-types", "host": [ "{{base_url}}" ], "path": [ "v1", "issuers", "{{issuer_id}}", "document-types" ] }, "description": "Returns the active document types enabled for the specified issuer." } }, { "name": "Add Document Type", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/issuers/{{issuer_id}}/document-types", "host": [ "{{base_url}}" ], "path": [ "v1", "issuers", "{{issuer_id}}", "document-types" ] }, "body": { "mode": "raw", "raw": "{\n \"documentType\": \"01\"\n}", "options": { "raw": { "language": "json" } } }, "description": "Enables a document type for the specified issuer. If the type was previously removed, it is reactivated. Returns the full updated list of active document types." } }, { "name": "Remove Document Type", "request": { "method": "DELETE", "url": { "raw": "{{base_url}}/v1/issuers/{{issuer_id}}/document-types/01", "host": [ "{{base_url}}" ], "path": [ "v1", "issuers", "{{issuer_id}}", "document-types", "01" ] }, "description": "Disables a document type for the specified issuer. The last active type cannot be removed." } } ] }, { "name": "Documents", "description": "Document lifecycle endpoints. All requests require a Bearer API key and the `X-Issuer-Id` header identifying the issuing branch.", "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{api_key}}", "type": "string" } ] }, "item": [ { "name": "Create Invoice", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" }, { "key": "Idempotency-Key", "value": "{{$guid}}", "description": "Optional — unique per intended invoice. Reuse on retries to avoid duplicates." } ], "url": { "raw": "{{base_url}}/v1/documents", "host": [ "{{base_url}}" ], "path": [ "v1", "documents" ] }, "body": { "mode": "raw", "raw": "{\n \"documentType\": \"01\",\n \"issueDate\": \"05/04/2026\",\n \"buyer\": {\n \"idType\": \"05\",\n \"id\": \"1234567890\",\n \"name\": \"John Doe\",\n \"email\": \"john@example.com\",\n \"address\": \"Av. Amazonas 123\"\n },\n \"items\": [\n {\n \"mainCode\": \"PROD-001\",\n \"auxiliaryCode\": \"AUX-001\",\n \"description\": \"Web development service\",\n \"quantity\": \"1.00\",\n \"unitPrice\": \"100.00\",\n \"discount\": \"0.00\",\n \"taxes\": [\n {\n \"code\": \"2\",\n \"rateCode\": \"2\",\n \"rate\": \"15.00\",\n \"taxableBase\": \"100.00\",\n \"taxAmount\": \"15.00\"\n }\n ]\n }\n ],\n \"payments\": [\n {\n \"method\": \"01\",\n \"total\": \"115.00\",\n \"term\": 30,\n \"termUnit\": \"dias\"\n }\n ],\n \"additionalInfo\": [\n { \"name\": \"Contract\", \"value\": \"CTR-2026-001\" }\n ]\n}", "options": { "raw": { "language": "json" } } }, "description": "Creates, XSD-validates, and signs a new electronic invoice. Returns the document with status `SIGNED`.\n\nAfter a successful response, copy the `accessKey` value into the `access_key` collection variable.\n\nThe `X-Issuer-Id` header identifies which branch and certificate to use. Get the id from `GET /v1/issuers`.\n\nThe `Idempotency-Key` header makes creation idempotent — generate it once per intended invoice and reuse on retries." } }, { "name": "List Documents", "request": { "method": "GET", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents?page=1&limit=10", "host": [ "{{base_url}}" ], "path": [ "v1", "documents" ], "query": [ { "key": "page", "value": "1", "description": "Page number (default: 1)", "disabled": true }, { "key": "limit", "value": "10", "description": "Results per page, 1-100 (default: 10)", "disabled": true }, { "key": "status", "value": "AUTHORIZED", "description": "Filter by status: SIGNED, RECEIVED, RETURNED, AUTHORIZED, NOT_AUTHORIZED", "disabled": true }, { "key": "documentType", "value": "01", "description": "Filter by document type code (e.g., 01)", "disabled": true }, { "key": "from", "value": "01/01/2026", "description": "Filter by issue date >= DD/MM/YYYY", "disabled": true }, { "key": "to", "value": "31/12/2026", "description": "Filter by issue date <= DD/MM/YYYY", "disabled": true } ] }, "description": "Lists documents for the specified issuer with optional filtering and pagination. Returns documents sorted by creation date (newest first)." } }, { "name": "Get Document", "request": { "method": "GET", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/{{access_key}}", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "{{access_key}}" ] }, "description": "Retrieves document metadata by access key. Does not call the SRI — use Check Authorization for status updates." } }, { "name": "Send to SRI", "request": { "method": "POST", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/{{access_key}}/send", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "{{access_key}}", "send" ] }, "description": "Submits the signed XML to the SRI reception service. Document moves to `RECEIVED` or `RETURNED`." } }, { "name": "Check Authorization", "request": { "method": "GET", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/{{access_key}}/authorize", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "{{access_key}}", "authorize" ] }, "description": "Queries the SRI authorization service. Document moves to `AUTHORIZED` or `NOT_AUTHORIZED`. On authorization, an email with the RIDE PDF and XML is sent to the buyer." } }, { "name": "Rebuild Invoice", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/{{access_key}}/rebuild", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "{{access_key}}", "rebuild" ] }, "body": { "mode": "raw", "raw": "{\n \"documentType\": \"01\",\n \"buyer\": {\n \"idType\": \"05\",\n \"id\": \"1234567890\",\n \"name\": \"John Doe\",\n \"email\": \"john@example.com\",\n \"address\": \"Av. Amazonas 123\"\n },\n \"items\": [\n {\n \"mainCode\": \"PROD-001\",\n \"description\": \"Web development service (corrected)\",\n \"quantity\": \"1.00\",\n \"unitPrice\": \"100.00\",\n \"discount\": \"0.00\",\n \"taxes\": [\n {\n \"code\": \"2\",\n \"rateCode\": \"2\",\n \"rate\": \"15.00\",\n \"taxableBase\": \"100.00\",\n \"taxAmount\": \"15.00\"\n }\n ]\n }\n ],\n \"payments\": [\n {\n \"method\": \"01\",\n \"total\": \"115.00\"\n }\n ]\n}", "options": { "raw": { "language": "json" } } }, "description": "Corrects and re-signs a document in `RETURNED` or `NOT_AUTHORIZED` status. Keeps the original `accessKey`, `sequential`, and `issueDate`. Send again after rebuilding." } }, { "name": "Get RIDE PDF", "request": { "method": "GET", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/{{access_key}}/ride", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "{{access_key}}", "ride" ] }, "description": "Downloads the RIDE PDF for an `AUTHORIZED` document. Returns `application/pdf`." } }, { "name": "Get XML", "request": { "method": "GET", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/{{access_key}}/xml", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "{{access_key}}", "xml" ] }, "description": "Downloads the authorization XML for an `AUTHORIZED` document, or the signed XML for earlier statuses. Returns `application/xml`." } }, { "name": "Get Events", "request": { "method": "GET", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/{{access_key}}/events", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "{{access_key}}", "events" ] }, "description": "Returns the full audit trail for the document — every lifecycle transition, SRI call, and email event." } }, { "name": "Retry Single Email", "request": { "method": "POST", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/{{access_key}}/email-retry", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "{{access_key}}", "email-retry" ], "query": [ { "key": "force", "value": "true", "description": "Set to true to resend an email that was already successfully delivered", "disabled": true } ] }, "description": "Retries the authorization email for a single document. Document must be `AUTHORIZED` with `email_status` of `PENDING` or `FAILED`. Add `?force=true` to resend an already-delivered email." } }, { "name": "Retry Emails (batch)", "request": { "method": "POST", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Numeric id of the issuing branch (from GET /v1/issuers)" } ], "url": { "raw": "{{base_url}}/v1/documents/email-retry", "host": [ "{{base_url}}" ], "path": [ "v1", "documents", "email-retry" ] }, "description": "Retries all `PENDING` and `FAILED` authorization emails across all documents for the specified issuer. Maximum 100 per call." } } ] }, { "name": "Catalogs", "description": "Reference data for SRI-defined codes. Use these to build valid invoice requests. Require a Bearer API key.", "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{api_key}}", "type": "string" } ] }, "item": [ { "name": "ID Types", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/catalogs/id-types", "host": [ "{{base_url}}" ], "path": [ "v1", "catalogs", "id-types" ] }, "description": "Returns buyer identification types (e.g. 04 = RUC, 05 = Cédula, 06 = Pasaporte). Use the `code` as `buyer.idType` on invoice creation." } }, { "name": "Payment Methods", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/catalogs/payment-methods", "host": [ "{{base_url}}" ], "path": [ "v1", "catalogs", "payment-methods" ] }, "description": "Returns SRI payment method codes. Use the `code` as `payments[].method` on invoice creation." } }, { "name": "Term Units", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/catalogs/term-units", "host": [ "{{base_url}}" ], "path": [ "v1", "catalogs", "term-units" ] }, "description": "Returns accepted SRI payment term units (e.g. dias, meses). Use the `code` as `payments[].termUnit` (with `payments[].term`) when sending invoices a plazos." } }, { "name": "Tax Types", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/catalogs/tax-types", "host": [ "{{base_url}}" ], "path": [ "v1", "catalogs", "tax-types" ] }, "description": "Returns SRI tax type codes (e.g. 2 = IVA, 3 = ICE, 5 = IRBPNR). Use the `code` as `taxes[].code` on invoice items." } }, { "name": "Tax Rates", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/catalogs/tax-rates", "host": [ "{{base_url}}" ], "path": [ "v1", "catalogs", "tax-rates" ] }, "description": "Returns tax rate codes grouped by tax type. Use `taxCode` as `taxes[].code` and `rateCode` as `taxes[].rateCode` on invoice items." } } ] }, { "name": "Notifications", "description": "Tenant-level alerts. Both read and unread are returned; use `readAt` to decide what to show. Use `?sinceId=` for efficient catch-up polling — pass the highest id seen on the last request to receive only new notifications.\n\nAll requests require a Bearer API key.", "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{api_key}}", "type": "string" } ] }, "item": [ { "name": "List Notifications", "request": { "method": "GET", "header": [ { "key": "X-Issuer-Id", "value": "{{issuer_id}}", "description": "Optional — filter to a specific issuer's notifications plus tenant-level ones. Omit for all-tenant view.", "disabled": true } ], "url": { "raw": "{{base_url}}/v1/notifications", "host": ["{{base_url}}"], "path": ["v1", "notifications"], "query": [ { "key": "sinceId", "value": "0", "description": "Optional — return only notifications with id > sinceId. Store the highest id seen and pass it on the next poll to catch up efficiently.", "disabled": true } ] }, "description": "Returns active (unexpired) notifications for the tenant, newest first. Both read and unread are included.\n\n**Polling pattern:** call on a schedule (e.g. every 60 s). Use `?sinceId=` to retrieve only notifications created after your last poll — store the highest `id` you've seen and pass it on the next request.\n\nOptional `X-Issuer-Id` header restricts results to a specific issuer's notifications plus tenant-level ones (`issuerId: null`). Omit it for an all-tenant view." } }, { "name": "Mark as Read", "request": { "method": "POST", "header": [], "url": { "raw": "{{base_url}}/v1/notifications/{{notification_id}}/read", "host": ["{{base_url}}"], "path": ["v1", "notifications", "{{notification_id}}", "read"] }, "description": "Marks a notification as read at the tenant level. Call this only when every user with access to the notification has acknowledged it on the frontend side. After this call, the notification no longer contributes to `unreadCount` on future polls.\n\nCopy a notification `id` from the List Notifications response into the `notification_id` variable." } }, { "name": "Get Preferences", "request": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/v1/notifications/preferences", "host": ["{{base_url}}"], "path": ["v1", "notifications", "preferences"] }, "description": "Returns the full notification preference list for the tenant — one entry per notification type. Types the tenant has never explicitly configured default to `enabled: true` (opt-out model)." } }, { "name": "Update Preferences", "request": { "method": "PATCH", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/notifications/preferences", "host": ["{{base_url}}"], "path": ["v1", "notifications", "preferences"] }, "body": { "mode": "raw", "raw": "[\n { \"type\": \"DOCUMENT_AUTHORIZED\", \"enabled\": true },\n { \"type\": \"CERT_EXPIRING\", \"enabled\": true },\n { \"type\": \"CERT_EXPIRED\", \"enabled\": true }\n]", "options": { "raw": { "language": "json" } } }, "description": "Bulk-upsert notification preferences for the tenant. Send only the types you want to change — unmentioned types are unchanged.\n\nWhen `enabled` is `false` for a type, no new notifications of that type are created. Existing unread notifications remain and can still be marked as read.\n\nValid types: `DOCUMENT_AUTHORIZED`, `CERT_EXPIRING`, `CERT_EXPIRED`, `SRI_SUBMISSION_FAILED`, `EMAIL_DELIVERY_FAILED`, `QUOTA_WARNING`." } } ] }, { "name": "Webhooks", "description": "Register HTTPS callback URLs to receive event notifications in near-real time. When a notification is created or updated, the API immediately POSTs a signed payload to every subscribed active endpoint.\n\n**Secret handling:** the signing secret is returned only at registration. Store it immediately — it cannot be retrieved later. If lost, deregister and re-register the endpoint.\n\n**Tier limits:** FREE: 1 endpoint, STARTER: 2, GROWTH: 5, BUSINESS: 10.\n\nAll requests require a Bearer API key.", "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{api_key}}", "type": "string" } ] }, "item": [ { "name": "Register Webhook", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/webhooks", "host": ["{{base_url}}"], "path": ["v1", "webhooks"] }, "body": { "mode": "raw", "raw": "{\n \"url\": \"https://app.example.com/v1/comprobify/events\",\n \"eventTypes\": [\"DOCUMENT_AUTHORIZED\", \"CERT_EXPIRING\", \"CERT_EXPIRED\"]\n}", "options": { "raw": { "language": "json" } } }, "description": "Registers a new webhook endpoint and returns the signing `secret` — **shown only once**. Store it immediately; it is used to verify `X-Comprobify-Signature` on incoming webhook requests.\n\n`url` must be a valid HTTPS URL.\n\n`eventTypes` is optional. Omit it or pass `[]` to subscribe to **all** event types. Valid values: `DOCUMENT_AUTHORIZED`, `CERT_EXPIRING`, `CERT_EXPIRED`, `SRI_SUBMISSION_FAILED`, `EMAIL_DELIVERY_FAILED`, `QUOTA_WARNING`.\n\nCopy the returned `id` into the `webhook_id` collection variable." } }, { "name": "List Webhooks", "request": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/v1/webhooks", "host": ["{{base_url}}"], "path": ["v1", "webhooks"] }, "description": "Returns all active webhook endpoints for the tenant. Signing secrets are never included in list responses." } }, { "name": "Update Webhook", "request": { "method": "PATCH", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/webhooks/{{webhook_id}}", "host": ["{{base_url}}"], "path": ["v1", "webhooks", "{{webhook_id}}"] }, "body": { "mode": "raw", "raw": "{\n \"url\": \"https://app.example.com/v1/comprobify/events-v2\",\n \"eventTypes\": [\"DOCUMENT_AUTHORIZED\"],\n \"active\": true\n}", "options": { "raw": { "language": "json" } } }, "description": "Updates a webhook endpoint's URL, event subscriptions, or active flag. All fields are optional — send only what you want to change.\n\nCopy an endpoint `id` from the List Webhooks response into the `webhook_id` variable." } }, { "name": "Deregister Webhook", "request": { "method": "DELETE", "header": [], "url": { "raw": "{{base_url}}/v1/webhooks/{{webhook_id}}", "host": ["{{base_url}}"], "path": ["v1", "webhooks", "{{webhook_id}}"] }, "description": "Soft-deletes a webhook endpoint (`active = false`). The endpoint stops receiving deliveries immediately. Past delivery records are preserved for audit purposes.\n\nCopy an endpoint `id` from the List Webhooks response into the `webhook_id` variable." } } ] }, { "name": "Admin", "description": "Admin endpoints for managing tenants, issuers, and API keys. Require the `ADMIN_SECRET` as a Bearer token.", "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{admin_secret}}", "type": "string" } ] }, "item": [ { "name": "Create Tenant", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/admin/tenants", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "tenants" ] }, "body": { "mode": "raw", "raw": "{\n \"email\": \"client@example.com\",\n \"subscriptionTier\": \"FREE\"\n}", "options": { "raw": { "language": "json" } } }, "description": "Create a tenant for manual onboarding. Status is set to ACTIVE automatically — no email verification required.\n\nCopy the returned `id` into the `tenant_id` collection variable." } }, { "name": "List Tenants", "request": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/v1/admin/tenants", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "tenants" ] } } }, { "name": "Update Tenant Tier", "request": { "method": "PATCH", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/admin/tenants/{{tenant_id}}/tier", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "tenants", "{{tenant_id}}", "tier" ] }, "body": { "mode": "raw", "raw": "{\n \"subscriptionTier\": \"STARTER\"\n}", "options": { "raw": { "language": "json" } } } } }, { "name": "Update Tenant Status", "request": { "method": "PATCH", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/admin/tenants/{{tenant_id}}/status", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "tenants", "{{tenant_id}}", "status" ] }, "body": { "mode": "raw", "raw": "{\n \"status\": \"SUSPENDED\"\n}", "options": { "raw": { "language": "json" } } }, "description": "Activate or suspend a tenant. Accepted values: `ACTIVE`, `SUSPENDED`." } }, { "name": "Manually Verify Tenant", "request": { "method": "POST", "header": [], "url": { "raw": "{{base_url}}/v1/admin/tenants/{{tenant_id}}/verify", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "tenants", "{{tenant_id}}", "verify" ] }, "description": "Manually activate a tenant's email when email delivery is not configured or for support purposes." } }, { "name": "Promote Tenant (admin)", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/admin/tenants/{{tenant_id}}/promote", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "tenants", "{{tenant_id}}", "promote" ] }, "body": { "mode": "raw", "raw": "{\n \"initialSequentials\": [\n { \"issuerId\": 1, \"documentType\": \"01\", \"sequential\": 1 }\n ]\n}", "options": { "raw": { "language": "json" } } }, "description": "Admin override for tenant promotion — skips the ACTIVE status check (does not require verified email). One-way action.\n\n`initialSequentials` is optional. If omitted, all active document types on all issuers start at sequential 1.\n\nAll sandbox API keys are revoked and replaced with production keys. The response includes all new production tokens — store them immediately." } }, { "name": "Mint API Key (admin)", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "url": { "raw": "{{base_url}}/v1/admin/tenants/{{tenant_id}}/api-keys", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "tenants", "{{tenant_id}}", "api-keys" ] }, "body": { "mode": "raw", "raw": "{\n \"label\": \"Initial key\",\n \"environment\": \"sandbox\"\n}", "options": { "raw": { "language": "json" } } }, "description": "Generates a new tenant-scoped API key. The plaintext token is returned only once — store it immediately.\n\n`label` is optional (max 100 chars). `environment` is `\"sandbox\"` (default) or `\"production\"`.\n\nCopy the returned key value into the `api_key` collection variable." } }, { "name": "Create Issuer (P12 upload)", "request": { "method": "POST", "url": { "raw": "{{base_url}}/v1/admin/issuers", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "issuers" ] }, "body": { "mode": "formdata", "formdata": [ { "key": "tenantId", "value": "{{tenant_id}}", "type": "text", "description": "Numeric tenant id (required)" }, { "key": "ruc", "value": "1700000000001", "type": "text", "description": "13-digit RUC" }, { "key": "businessName", "value": "Acme S.A.", "type": "text" }, { "key": "tradeName", "value": "Acme", "type": "text", "description": "Optional trade name", "disabled": true }, { "key": "mainAddress", "value": "Av. Amazonas 123, Quito", "type": "text", "description": "Main (fiscal) address" }, { "key": "branchAddress", "value": "Av. Amazonas 123, Quito", "type": "text", "description": "Branch address (optional — defaults to mainAddress if omitted)", "disabled": true }, { "key": "branchCode", "value": "001", "type": "text", "description": "3-digit branch code" }, { "key": "issuePointCode", "value": "001", "type": "text", "description": "3-digit issue point code" }, { "key": "emissionType", "value": "1", "type": "text", "description": "Always 1 (normal emission)" }, { "key": "requiredAccounting", "value": "false", "type": "text", "description": "true if the issuer is required to keep accounting (obligado a llevar contabilidad)" }, { "key": "specialTaxpayer", "value": "", "type": "text", "description": "Special taxpayer resolution number (optional)", "disabled": true }, { "key": "certPassword", "value": "your-p12-password", "type": "text" }, { "key": "cert", "type": "file", "src": "", "description": "P12 certificate file" }, { "key": "documentTypes", "value": "[\"01\"]", "type": "text", "description": "Optional. Document type codes to enable (default: [\"01\"])", "disabled": true }, { "key": "initialSequentials", "value": "[{\"documentType\":\"01\",\"sequential\":1}]", "type": "text", "description": "Optional — pre-seed sequential counters. Example: [{\"documentType\":\"01\",\"sequential\":500}] means the first invoice will be 000000500.", "disabled": true } ] }, "description": "Creates a new issuer for a tenant by uploading a P12 certificate. Requires `tenantId`. **Does NOT return an API key** — mint one separately via `POST /v1/admin/tenants/:id/api-keys`.\n\nAfter a successful response, copy the issuer `id` into `issuer_id`." } }, { "name": "Create Issuer (branch copy)", "request": { "method": "POST", "url": { "raw": "{{base_url}}/v1/admin/issuers", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "issuers" ] }, "body": { "mode": "formdata", "formdata": [ { "key": "tenantId", "value": "{{tenant_id}}", "type": "text", "description": "Numeric tenant id (required)" }, { "key": "sourceIssuerId", "value": "{{issuer_id}}", "type": "text", "description": "ID of the issuer to copy the certificate from" }, { "key": "ruc", "value": "1700000000001", "type": "text" }, { "key": "businessName", "value": "Acme S.A.", "type": "text" }, { "key": "tradeName", "value": "Acme", "type": "text", "description": "Optional trade name", "disabled": true }, { "key": "mainAddress", "value": "Av. Naciones Unidas 456, Quito", "type": "text", "description": "Main (fiscal) address" }, { "key": "branchAddress", "value": "Av. Naciones Unidas 456, Quito", "type": "text", "description": "Branch address (optional — defaults to mainAddress if omitted)", "disabled": true }, { "key": "branchCode", "value": "002", "type": "text", "description": "Different branch code from the source issuer" }, { "key": "issuePointCode", "value": "001", "type": "text" }, { "key": "emissionType", "value": "1", "type": "text" }, { "key": "requiredAccounting", "value": "false", "type": "text", "description": "true if the issuer is required to keep accounting" }, { "key": "specialTaxpayer", "value": "", "type": "text", "description": "Special taxpayer resolution number (optional)", "disabled": true }, { "key": "initialSequentials", "value": "[{\"documentType\":\"01\",\"sequential\":1}]", "type": "text", "description": "Optional — pre-seed sequential counters. Example: [{\"documentType\":\"01\",\"sequential\":500}]", "disabled": true } ] }, "description": "Creates a new branch issuer by copying the certificate from an existing issuer. No P12 file needed. Requires `tenantId`. **Does NOT return an API key** — mint one separately via `POST /v1/admin/tenants/:id/api-keys`." } }, { "name": "List Issuers (admin)", "request": { "method": "GET", "url": { "raw": "{{base_url}}/v1/admin/issuers", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "issuers" ] }, "description": "Returns all active issuers across all tenants." } }, { "name": "Revoke API Key", "request": { "method": "DELETE", "url": { "raw": "{{base_url}}/v1/admin/api-keys/{{api_key_id}}", "host": [ "{{base_url}}" ], "path": [ "v1", "admin", "api-keys", "{{api_key_id}}" ] }, "description": "Revokes (soft-deletes) an API key by ID. The key stops working immediately." } }, { "name": "Run Notification Jobs", "request": { "method": "POST", "header": [], "url": { "raw": "{{base_url}}/v1/admin/jobs/notifications", "host": ["{{base_url}}"], "path": ["v1", "admin", "jobs", "notifications"] }, "description": "Runs all periodic notification jobs across every non-suspended tenant:\n\n1. **Certificate expiry checks** — inspects `cert_expiry` for every active issuer and upserts `CERT_EXPIRING` / `CERT_EXPIRED` alerts. Auto-dismisses alerts when a cert has been renewed (> 30 days remaining).\n\n2. **Webhook retry queue** — retries all `RETRYING` webhook deliveries past their scheduled `next_retry_at` time.\n\nThis endpoint is designed to be called by an external scheduler (cron, infrastructure job) on a regular interval — every 5 minutes is a reasonable default. The job is **idempotent**: running it multiple times is safe.\n\nResponse includes `tenantsChecked` and a `retries` breakdown (`attempted`, `succeeded`, `failed`, `exhausted`)." } } ] }, { "name": "Health", "description": "Monitoring endpoint. No authentication required.", "item": [ { "name": "Health Check", "request": { "auth": { "type": "noauth" }, "method": "GET", "url": { "raw": "{{base_url}}/health", "host": [ "{{base_url}}" ], "path": [ "health" ] }, "description": "Returns `{ status: \"ok\", uptime }` (200) when the database is reachable, or `{ status: \"error\", uptime }` (503) when it is not. Use for load balancer health checks and container liveness probes." } } ] } ] }