Issuance flow
From quote to issued policy: quote, issue, resolve conditions and get the documents.
Step by step
- Quote with
POST /api/quotes: returns thequoteIdand the coverages with their net premium and total premium. - If the quote is
pending_approval, wait for the insurer's approval (quote.approvedwebhook). - Issue with
POST /api/policiessendingquoteId, the chosencoverageIdand the issuance data. - If the product uses conditional issuance, the policy stays
pendinguntil its conditions are resolved withPOST /api/policies/{id}/conditions/resolve; only then does the payment open (payment_url). - Get the documents: the certificate is generated automatically; you can also render or regenerate PDFs.
1. Quote
- Requires a key with the
quotescope. Body:productIdandvariables(the product variables that affect the price). Each product reference lists its variables, types, ranges and options. - Answers
200with the priced quote. - The quote expires according to the product (
quoteTtlHours, 48 hours by default): seeexpiresAt. status:priced, orpending_approvalwhen a coverage requires approval.- Amounts:
netPremiumis the net premium (technical price, before taxes) andtotalPremiumis the total premium (amount to pay).
Request
curl -X POST https://api.quore.app/api/quotes \
-H "x-api-key: sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"productId": "PRODUCT_ID",
"variables": { "edad": 35, "plan": "full" }
}'Response (abridged)
{
"quoteId": "3f1c2a9e-5b7d-4c1e-9a1f-2d6b8e0c4a11",
"status": "priced",
"expiresAt": "2026-10-28T14:00:00.000Z",
"term_type": "annual_installments",
"coverages": [
{
"key": "basica",
"name": "Basic coverage",
"currency": "ARS",
"netPremium": 42000,
"taxes": 8820,
"totalPremium": 50820,
"installments": 12,
"installment": 4235
}
]
}2. Issue
- Requires a key with the
emitscope. Body:quoteId,coverageId(thekeyof one of the quote coverages) anddata. datacarries the issuance-step variables and the payment method. Quoted values are taken from the quote.- Answers
201with the policy. Retrying with the same quote and the same coverage returns the same policy; another coverage answers409. - With an online payment provider the response includes
paymentto complete the charge (see Payments). - Manual payment method: never send the full card number or the CVV; only
card_last4,card_brandandcard_holder.
Request
curl -X POST https://api.quore.app/api/policies \
-H "x-api-key: sk_test_…" \
-H "Content-Type: application/json" \
-d '{
"quoteId": "QUOTE_ID",
"coverageId": "basica",
"data": {
"nombre": "Ana Pérez",
"payment": { "method": "credit_card", "card_last4": "4242", "card_brand": "visa", "card_holder": "Ana Pérez" }
}
}'3. Issuance conditions
- With conditional issuance the policy is created
pending, with itsconditionslist (for example, an inspection). - Resolve each condition with
statusapprovedorfailed(andfailureReason). The insurer can also resolve them from its dashboard. - With all approved (or one, when the logic is OR) the policy becomes
active; a failed condition withrejectbehavior makes itrejected. - Conditions are resolved BEFORE payment: while they're pending no charge is started (
payment_after_conditions: trueon issuance). Once they're all approved, if the product collects online (Stripe, Mercado Pago or Rebill), the policy moves topending_payment, thepolicy.status_changedwebhook and the resolve response carrypayment_url(the public payment page), and the insured and the intermediary get the link to pay. When the payment is confirmed the policy becomesactiveright away. Without online collection it goes straight toactive. A payment-type condition is not added when collecting online: the payment is the last step. - A declined payment leaves the policy
payment_failed("Payment declined", withpayment_status: failed) and firespayment.failed. The intermediary (or the admins when there's none) and the insured are notified with the reason and the same payment link to retry; an approved retry activates it. - A rejected (or expired) condition ends the flow with no charge: the policy becomes
rejectedand the payment never opens. Older policies paid before their conditions were resolved and then rejected stay flagged withrefund_status: pending(payment.refund_required) and are marked refunded in Approvals or in the policy detail (payment.refunded). - Optional expiry: with
issuance.expires_after_hours(orexpires_after_hourson a condition), a policy whose conditions are still pending when it expires is rejected with the reason "Vencida" (expired). Not configured: it never expires. - Concurrent resolutions of the same policy are applied one at a time, none is lost.
- Every status change fires the
policy.status_changedwebhook.
Resolve a condition
curl -X POST https://api.quore.app/api/policies/POLICY_ID/conditions/resolve \
-H "x-api-key: sk_test_…" \
-H "Content-Type: application/json" \
-d '{ "conditionId": "inspection", "status": "approved" }'4. Documents
- The certificate is generated automatically once the policy is
active(on issuance, or when a conditional policy activates) and sent to the insured. A pending or rejected policy has no certificate (409when requested). - The policy PDF is available when the product has its template and your plan includes that document.
- Documents of a sandbox record can only be read with a sandbox key.
| Endpoint | What it does |
|---|---|
| GET/api/policies/{id}/documents | Lists the policy generated documents (id, docType, generatedAt). |
| GET/api/documents/{id}/download | Downloads a document: returns a temporary URL or the PDF. |
| GET/api/documents/render | Renders a policy or quote PDF on the fly (docType + policyId or quoteId), without storing it. |
| POST/api/policies/{id}/documents/certificate/regenerate | Regenerates the certificate (answers 202 with a jobId). |
Documents
curl https://api.quore.app/api/policies/POLICY_ID/documents -H "x-api-key: sk_test_…" curl https://api.quore.app/api/documents/DOCUMENT_ID/download -H "x-api-key: sk_test_…" curl "https://api.quore.app/api/documents/render?docType=certificate&policyId=POLICY_ID" -H "x-api-key: sk_test_…" -o certificado.pdf
5. Read and update policies
To reconcile your system with Quore: list and read policies, record the number the insurer assigns, and get a quote's documents. admin includes every scope.
| Endpoint | Scope | Request | Response | Codes |
|---|---|---|---|---|
| GET/api/policies | read · emit | Optional query: productId, status, environment, limit (50 by default, 200 max), offset. | 200 with { data, total, limit, offset }, newest first. Each item is the policy with camelCase fields (policyNumberInternal, netPremium, createdAt…). | 400 environment of the other environment · 401 · 403 missing scope |
| GET/api/policies/{id} | read · emit | Path: the policy id. | 200 with the policy (same format as policy.created, with numeric amounts) and documents: id, doc_type, generated_at, download_path. | 401 · 403 missing scope · 404 not found or of the other environment |
| POST/api/policies/{id}/insurer-number | emit | Body: { "policyNumberInsurer": "AP-2026-000123" } (non-empty text). Replaces the previous value. | 200 with the updated policy (camelCase fields, as in the list). | 400 missing or empty · 401 · 403 missing scope · 404 not found or of the other environment |
| GET/api/quotes/{id}/documents | read · quote · emit | Path: the quote id. | 200 with the quote's documents: id, docType, policyId, quoteId, templateId, generatedAt, createdAt. | 401 · 403 publishable key or missing scope · 404 not found or of the other environment |
Requests
curl "https://api.quore.app/api/policies?status=active&limit=50&offset=0" -H "x-api-key: sk_test_…"
curl https://api.quore.app/api/policies/POLICY_ID -H "x-api-key: sk_test_…"
curl https://api.quore.app/api/quotes/QUOTE_ID/documents -H "x-api-key: sk_test_…"
curl -X POST https://api.quore.app/api/policies/POLICY_ID/insurer-number \
-H "x-api-key: sk_test_…" \
-H "Content-Type: application/json" \
-d '{ "policyNumberInsurer": "AP-2026-000123" }'GET /api/policies (abridged)
{
"data": [
{
"id": "b2a7…",
"policyNumberInternal": "POL-0042",
"policyNumberInsurer": null,
"status": "active",
"productId": "PRODUCT_ID",
"coverageId": "basica",
"quoteId": "QUOTE_ID",
"netPremium": 42000,
"totalPremium": 50820,
"currency": "ARS",
"environment": "live",
"createdAt": "2026-10-26T14:03:11.402Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}GET /api/policies/{id} (abridged)
{
"id": "b2a7…",
"policy_number_internal": "POL-0042",
"policy_number_insurer": "AP-2026-000123",
"status": "active",
"product_id": "PRODUCT_ID",
"coverage_id": "basica",
"quote_id": "QUOTE_ID",
"net_premium": 42000,
"total_premium": 50820,
"currency": "ARS",
"installments": 12,
"installment_amount": 4235,
"environment": "live",
"payment_method_status": null,
"start_date": "2026-10-26T00:00:00.000Z",
"end_date": "2027-10-26T00:00:00.000Z",
"documents": [
{
"id": "DOCUMENT_ID",
"doc_type": "certificate",
"generated_at": "2026-10-26T14:03:15.020Z",
"download_path": "/api/documents/DOCUMENT_ID/download"
}
]
}- A key only sees records of its own environment: a test key does not find live policies (
404). - Publishable (widget) keys cannot use any of these endpoints (
403).
Approvals
- Quote: a coverage that requires approval (always or under a condition) leaves the quote
pending_approval. The insurer approves or rejects it from its dashboard; it firesquote.approvedorquote.rejected. - Intermediaries: when the product does not let them issue directly, an intermediary's quote stays
pending_approvaluntil the insurer approves it; once approved it can be issued, and the policy is issued as soon as the payment is confirmed. - A
pending_approval, expired or rejected quote cannot be issued (400).
Statuses
Quote
| Status | Meaning |
|---|---|
draft | Draft, not priced yet. |
priced | Priced: ready to issue until expiresAt. |
pending_approval | Needs the insurer approval before it can be issued and paid (a coverage rule, or an intermediary quote without direct issuance). The reason is in result.approval_reasons. |
approved | Approved: can be issued. |
rejected | Rejected: cannot be issued. |
accepted | Accepted by the client. |
expired | Expired: quote again. |
issued | Issued: it already has its policy. |
Policy
| Status | Meaning |
|---|---|
pending | Waiting for issuance conditions (for example, an inspection). |
pending_payment | Issued, waiting for the online payment to complete (on conditional products, once its conditions are approved). |
active | Active. Depending on its dates it shows as Contracted (starts later), In force or Expired. |
rejected | Rejected (failed or expired condition). |
cancelled | Cancelled. |
expired | Expired. |
Recurring and usage policies also report payment_method_status: pending until the insured saves a payment method, then active. The policy only becomes active with its first charge confirmed (recurring) or its card validated (usage).
Errors
Every error answers JSON with error (a readable message in Spanish). Per-field validation errors (422) add fields: variable key → message.
| Code | When |
|---|---|
400 | Missing or invalid data: no productId/variables, inactive product or disabled channel, expired, rejected or pending-approval quote, missing required attachment, list of another environment. |
401 | Missing, invalid or revoked key; missing or expired emission token (widget). |
403 | Insufficient scope, publishable key on a read resource, inactive intermediary, intermediary without a commission tier for the product, or feature not included in your plan. |
404 | Does not exist, belongs to another account or to the other environment (a test key only sees test data). |
409 | Conflict: the quote was already issued with another coverage, or a unique value already exists (for example, externalRef). |
422 | Per-field validation (fields), payment method not enabled for the product, or usage not billable for the policy. |
429 | Request limit reached: wait the Retry-After seconds. |
422: per-field validation
{
"error": "Age: the minimum value is 18",
"fields": {
"edad": "Age: the minimum value is 18",
"plan": "Plan: this field is required"
}
}Error
{
"error": "Quote not found"
}