Capturia measures its ads and site traffic with the Meta pixel and Google Analytics. You can decline at any time. Learn more

FR

Quotes

4 endpoints

get/v1/quotes

List quotes

Returns the paginated list of the account's quotes (cursor v2). Stable sort created_at DESC, id DESC.

Archived quotes (archived_at IS NOT NULL) are excluded by default — the public API does not expose them. To retrieve an archived quote, use the dashboard.

Filters

  • ?status — exact match (draft, sent, signed, cancelled)
Required scopes: quotes:read

Query parameters

NameTypeRequiredDescription
statusstringoptionalExact match on the quote lifecycle.

Responses

StatusDescription
200

Paginated list of quotes.

400

The request is malformed (missing header, invalid JSON, invalid parameter).

401

Unknown API key or invalid format.

403

Authorization denied. Two causes, distinguished by code: insufficient_scope (the key lacks the required scope — details.required_scope) or feature_not_available (the public API or the targeted module isn't included in the account's plan — details.feature names the feature, e.g. public_api, quotes). A plan denial is final for the key: don't retry; the account must change plans.

429

The per-window request quota has been exceeded for this API key or endpoint.

500

Unexpected server error. Always include the request_id when contacting support.

Example request

curl -X GET 'https://api.capturia.io/v1/quotes' \
  -H 'Authorization: Bearer cap_live_<your-key>' \
  -H 'Content-Type: application/json'
post/v1/quotes

Create a quote

Creates a draft quote (status="draft") through the same creation path as the dashboard: Capturia assigns the number (quote_number, the account's sequence, never a duplicate) and applies the account's defaults (validity, payment terms, tax profile, deposit policy). A provided quote_number is rejected (400 validation_error, reason read_only).

contact_id links the quote to its contact (/v1/contacts): each omitted prospect_* field is read from the contact, and each field you provide takes precedence. line_items writes the line items in the same operation; the server computes the amounts. To send the quote to the lead, use POST /v1/quotes/{id}/send.

Idempotency

The Idempotency-Key header is optional but recommended: a retry after a timeout never creates a second quote. A second request with the same key and the same payload returns the original response; the same key with a different payload returns 409 idempotency_conflict. A rejection (400, 403, 404) is not stored: the key remains usable.

Required scopes: quotes:write

Request body

PropertyTypeRequiredDescription
contact_idstring | nulloptionalThe account's contact (/v1/contacts) to link the quote to. Each omitted prospect_* field is read from the contact; each field you provide takes precedence. A contact from another account → 400 validation_error (reason not_found_in_tenant).
prospect_namestring | nulloptional—
prospect_emailstring | nulloptional—
prospect_phonestring | nulloptional—
prospect_companystring | nulloptional—
prospect_addressstring | nulloptionalFree-form mailing address.
languagestringoptionalLanguage of the quote (public page, PDF, sending email). Defaults to fr.
valid_untilstring | nulloptionalValidity deadline (YYYY-MM-DD). Omitted: the account's default validity (in days, starting today, Montreal time). null: no date.
payment_termsstring | nulloptionalPayment terms (free text). Omitted: the account's terms. null: none.
line_itemsobject[]optionalThe quote's line items, written in the same operation; the server computes the amounts.
internal_notesstringoptionalInternal notes, not visible to the prospect.
sales_rep_idstring | nulloptionalAssigned sales rep; null or omitted = none.
tax_profile_idstring | nulloptionalApplied tax profile (GST/QST); null or omitted = the account's default profile.
signature_payment_orderstringoptionalSignature ↔ payment order on the public page. Omitted → the account's default setting (snapshot taken at creation). Value outside the list → 400 validation_error.
tax_exempt_reasonstring | nulloptionalWritten reason for a tax-exempt sale (see Quote.tax_exempt_reason).

Responses

StatusDescription
201

Quote created.

400

Parameter or body validation failed.

401

Unknown API key or invalid format.

403

Authorization denied. Two causes, distinguished by code: insufficient_scope (the key lacks the required scope — details.required_scope) or feature_not_available (the public API or the targeted module isn't included in the account's plan — details.feature names the feature, e.g. public_api, quotes). A plan denial is final for the key: don't retry; the account must change plans.

409

A previous request with the same Idempotency-Key used a different payload, or is still being processed.

415

The Content-Type header is missing or not supported for this endpoint.

429

The per-window request quota has been exceeded for this API key or endpoint.

500

Unexpected server error. Always include the request_id when contacting support.

Example request

curl -X POST 'https://api.capturia.io/v1/quotes' \
  -H 'Authorization: Bearer cap_live_<your-key>' \
  -H 'Content-Type: application/json' \
  -d '{
  "contact_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
  "valid_until": "2026-06-30",
  "payment_terms": "Net 30",
  "line_items": [
    {
      "description": "Remplacement de la toiture",
      "quantity": 1,
      "unit_price": 9500
    }
  ]
}'
patch/v1/quotes/{id}

Update a quote

Updates the provided fields of the quote (omitted fields are kept). At least one field must be provided, otherwise 400 no_fields.

Amounts (subtotal, tps_amount, tvq_amount, total) are not editable via PATCH — they are computed server-side from the blocks (managed from the dashboard).

status only accepts declined (the lead declined it) and cancelled: a quote moves to sent through POST /v1/quotes/{id}/send (the email goes out), to viewed and signed through the lead, and to expired through its validity date. A signed, paid, or already closed quote can no longer be closed (409 status_locked). Closing must be done on its own: status combined with other fields returns 400 validation_error (reason exclusive_field).

Archived quotes (archived_at IS NOT NULL) cannot be modified — returns 404 resource_not_found.

Required scopes: quotes:write

Request body

PropertyTypeRequiredDescription
prospect_namestring | nulloptional—
prospect_emailstring | nulloptional—
prospect_phonestring | nulloptional—
prospect_companystring | nulloptional—
prospect_addressstring | nulloptional—
valid_untilstring | nulloptional—
payment_termsstring | nulloptional—
internal_notesstring | nulloptional—
sales_rep_idstring | nulloptional—
tax_profile_idstring | nulloptional—
payment_plan_typestringoptionalPayment plan structure. The amount of each installment is derived server-side. The plan is locked once the first payment is collected (409 payment_plan_locked). Consistency between fields (a deposit with no value, an installment count out of bounds) is validated server-side → 400 validation_error.
payment_deposit_typestring | nulloptional—
payment_deposit_valuenumber | nulloptional—
payment_installments_countinteger | nulloptional—
payment_interval_daysinteger | nulloptional—
payment_execution_modestringoptionalautomatic = installments are charged to the card off-session; manual = payments received (bank transfer, Interac) are recorded by the sales rep, and nothing is charged. Value outside the list → 400 validation_error.
signature_payment_orderstringoptionalSignature ↔ payment order on the public page. Editable while the quote is a draft; once it has been sent → 409 signature_payment_order_locked. Value outside the list → 400 validation_error.
tax_exempt_reasonstring | nulloptionalWritten reason for a tax-exempt sale; null makes the quote taxed again. An empty string or one longer than 200 characters → 400 validation_error. Totals are recalculated server-side when it changes.
statusstringoptionalCloses the quote: declined (the lead declined it) or cancelled. The other statuses are never set through PATCH: sent comes from POST /v1/quotes/{id}/send (the email goes out), viewed and signed from the lead, expired from the validity date. Value outside the list → 400 validation_error. A signed or paid quote can no longer be closed → 409 status_locked (nothing is written).

Responses

StatusDescription
200

Quote updated.

400

Parameter or body validation failed.

401

Unknown API key or invalid format.

403

Authorization denied. Two causes, distinguished by code: insufficient_scope (the key lacks the required scope — details.required_scope) or feature_not_available (the public API or the targeted module isn't included in the account's plan — details.feature names the feature, e.g. public_api, quotes). A plan denial is final for the key: don't retry; the account must change plans.

404

The requested resource doesn't exist (or isn't within the tenant's scope).

409

The request is syntactically valid but violates a business rule.

429

The per-window request quota has been exceeded for this API key or endpoint.

500

Unexpected server error. Always include the request_id when contacting support.

Example request

curl -X PATCH 'https://api.capturia.io/v1/quotes/{id}' \
  -H 'Authorization: Bearer cap_live_<your-key>' \
  -H 'Content-Type: application/json' \
  -d '{
  "prospect_name": "Alexandre Tremblay",
  "valid_until": "2026-07-31",
  "payment_terms": "Net 60"
}'
post/v1/quotes/{id}/send

Send a quote to the lead

Emails the quote to the lead, through the same service as the dashboard's “Send” button: sent from the assigned sales rep (otherwise from the business), with a link to the quote's public page, then status="sent" and sent_at are set. Only a draft can be sent; nothing goes out again for a quote that was already sent.

A quote without a contact (lead entered without a contact) is first linked to the lead's contact: found by their email or phone number, otherwise created — never a duplicate. The email then opens its thread in the Inbox. The created contact stays in the API key's account, and its creation triggers the automations and CRM sync of a new contact.

Rejections

  • 409 already_signed: the quote is signed.
  • 409 quote_cancelled: the quote was cancelled.
  • 409 already_sent: the quote was already sent.
  • 409 quote_status_changed: the quote is no longer a draft (for example, it expired).
  • 409 send_in_progress: a send of this quote is already in progress.
  • 409 send_outcome_unknown: a previous send could not be confirmed; it is not retried.
  • 409 recipient_unsubscribed: the lead unsubscribed.
  • 409 sender_unavailable: the assigned sales rep has no valid sending address.
  • 422 no_prospect_email / 422 invalid_prospect_email: the lead's email is missing or invalid.
  • 422 unpriced_lines, 422 untaxed_quote, 422 deposit_not_payable: content guard.

No body is required.

Required scopes: quotes:write

Request body

Optional body — no fields are used today (reserved for future use).

Responses

StatusDescription
200

Quote sent to the lead.

401

Unknown API key or invalid format.

403

Authorization denied. Two causes, distinguished by code: insufficient_scope (the key lacks the required scope — details.required_scope) or feature_not_available (the public API or the targeted module isn't included in the account's plan — details.feature names the feature, e.g. public_api, quotes). A plan denial is final for the key: don't retry; the account must change plans.

404

The requested resource doesn't exist (or isn't within the tenant's scope).

409

The request is syntactically valid but violates a business rule.

422

The request is syntactically valid but violates a business rule.

429

The per-window request quota has been exceeded for this API key or endpoint.

500

Unexpected server error. Always include the request_id when contacting support.

Example request

curl -X POST 'https://api.capturia.io/v1/quotes/{id}/send' \
  -H 'Authorization: Bearer cap_live_<your-key>' \
  -H 'Content-Type: application/json'