Proposal workflow
Approve held sends, void or delete proposals, manage e-signature requests, make customer copies, switch terms and payment terms, add library content, arrange items, apply designs, and manage proposal templates and clauses.
View as MarkdownEverything the proposal builder does around a proposal besides writing and sending it (Proposals). Each call runs the builder's own button as the user who created the API key, with that user's permissions: draft-only rules, the "out for signature" lock, owner-only approvals and the job timeline behave exactly as in the app. Calls that return a proposal return the same object as GET /v1/proposals/{id}.
A member's key acts as that member only. Proposal calls need the key's user to have pricing access.
Warning
POST /v1/proposals/{id}/resend-signature emails the signer. void, void-signature and DELETE /v1/proposals/{id} cancel an open e-signature request with the signing provider, and void/delete change what the customer sees at their link. PUT /v1/proposals/{id}/client-logo makes TruTec download the link you give it. Confirm with the person first, especially from an AI assistant.
Note
Partner keys that act on behalf of several workspaces must also pass firm_id (query string, or JSON body on writes).
Send approvals#
When someone sends a proposal over their approval limit, the send is held and the owners are asked to approve it (approval_status: pending; a send through the API answers 409 approval_required).
GET /v1/proposal-approvals lists held sends, newest first: proposal_id, title, job_id, amount, approval_limit, requested_by (id, name), requested_at. Permission: Read jobs.
POST /v1/proposals/{id}/approve-send approves; POST /v1/proposals/{id}/reject-send rejects, with an optional comment. Permission: Write jobs, and the key must belong to a workspace owner. The teammate who asked is emailed the decision; an approved proposal is not sent until someone sends it. 409 when nothing is waiting.
Void and delete#
POST /v1/proposals/{id}/void withdraws the proposal: the customer's link shows an "unavailable" page, an open e-signature request is cancelled with the provider, and the next send freezes a fresh version on the same link. 409 when it is already void; 502 when the provider couldn't cancel the signature request (nothing changed).
DELETE /v1/proposals/{id} deletes it from the job: it is voided, its customer link stops working for good, an open signature request is cancelled, and the deletion is logged on the job. Permission: Write jobs and Delete records. Returns 204.
E-signature requests#
POST /v1/proposals/{id}/resend-signature emails the signer the open signature request again. warning is set when some signers could not be reached. 404 when no request is out.
POST /v1/proposals/{id}/void-signature cancels the open signature request (optional reason, shown to the signer by the provider) and reopens the proposal for changes; it stays void on the same link until sent again.
GET /v1/proposals/{id}/signature shows the request's state (Proposals).
Customer copies#
POST /v1/proposals/{id}/customer-copies
For a job bid to several customers (each additional customer on the job): makes or refreshes a copy of the proposal for each, addressed to that customer. Copies already sent stay exactly as sent. Nothing is emailed; send each copy with POST /v1/proposals/{copy_id}/send. Returns data (the main proposal and each copy: proposal_id, is_main, customer, contact_name, contact_email, status, sent), created and refreshed.
Terms, payment terms and the content library#
GET /v1/terms-presets lists saved Terms sets (id, name, is_company_default, title, body_html, items, footer_html). GET /v1/payment-terms-presets lists payment terms (id, name, due_days, deposit_rate as a fraction, terms_line, summary such as "25% deposit · Net 30").
POST /v1/proposals/{id}/apply-terms-preset with preset_id (or company-default) points the proposal at that Terms set; the proposal's own terms edits are dropped.
POST /v1/proposals/{id}/apply-payment-terms-preset with preset_id applies the deposit, due days and terms line; null clears the pick.
GET /v1/proposal-library lists everything that can be added: sections, clauses, pdf_pages (company PDF pages), terms_presets, payment_terms_presets, each with the kind and id to pass.
POST /v1/proposals/{id}/add-from-library adds to a draft:
curl -X POST "https://trutec.ai/api/v1/proposals/3312/add-from-library" \
-H "X-API-Key: sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"items": [{"kind": "clause", "id": "cl-4e1a9b0c2d3f"}, {"kind": "pdf", "id": "5521"},
{"kind": "terms", "id": "tp-81c2d0e9aa41"}]}'
kind is section, clause, pdf, terms or payment_terms (at most one each of the last two). Returns the proposal and applied. Errors: 409 Only draft proposals can be edited. or when a picked item was removed from the library.
These four writes are refused (409) while the proposal is out for signature.
Item arrangement#
GET /v1/proposals/{id}/arrangement returns the proposal's items in customer order:
| Field | Description |
|---|---|
id |
The proposal item id (use it below). |
line_item_id |
The estimate line it shows. |
name |
|
phase |
Items in one phase stay together, e.g. Phase 1. |
item_number |
A bid item number, e.g. 2.1. |
optional |
An optional add-on instead of base bid. |
removable |
Base bid the customer can untick before accepting. |
group_heading |
Lines sharing a heading print as one priced group. |
alternate_name |
A named bid option (alternate), or empty. |
hidden_on_proposal |
Priced but not listed. |
and editable (false once sent or out for signature).
PATCH /v1/proposals/{id}/arrangement with items: each {"id": …} plus the fields to change. Items you leave out keep their settings; list every item to reorder them. A sub-line follows its parent line's phase. optional, group_heading and alternate_name change the estimate itself (it re-prices) and are refused once any proposal for that estimate went out: start an estimate revision first.
curl -X PATCH "https://trutec.ai/api/v1/proposals/3312/arrangement" \
-H "X-API-Key: sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"items": [{"id": 77120, "phase": "Phase 1", "item_number": "1.1"},
{"id": 77121, "optional": true}]}'
Returns the arrangement. Errors: 404 Proposal item … not found; 409 Create a draft revision before changing this proposal.
Design: starter templates, customer logo, walkthrough video#
GET /v1/proposal-starter-templates lists the ready-made designs (id, name, kind proposal or report, description). POST /v1/proposals/{id}/apply-starter-template with template_id puts one on a draft in your brand; company PDF pages already on it stay. To apply one of your own saved templates use POST /v1/proposals/{id}/apply-template (Proposals).
PUT /v1/proposals/{id}/client-logo with url (an http(s) link to a PNG, JPEG, GIF, WebP or SVG) puts the customer's logo on the cover. TruTec downloads it and keeps its own copy. DELETE removes it (204).
PUT /v1/proposals/{id}/walkthrough-video with video_url shows a walkthrough video from a link; DELETE removes it (204). Recording or uploading a video file is done in TruTec.
Proposal templates#
GET /v1/proposal-templates lists saved templates (Proposals). Permission for these writes: Write jobs, and the key's user needs an estimating role of owner, admin or estimator (Delete records to delete).
POST /v1/proposal-templates saves a proposal's layout as a template: proposal_id, name (up to 120 characters), optional description and pdf_page_ids (company PDF pages to include). The pages and their order, display choices, reusable sections and wording, and design are saved; never the customer, prices or job notes. Returns 201 with id, name, description, is_default.
PATCH /v1/proposal-templates/{id}: name, description; layout_from_proposal_id (plus optional pdf_page_ids) re-saves the layout from a proposal; is_default: true makes it the template every new proposal starts from (false stops that). Existing proposals never change.
DELETE /v1/proposal-templates/{id} removes it (204). If it was the default, new proposals go back to the workspace defaults.
Proposal clauses#
GET /v1/proposal-clauses lists the clause library. These writes need a key created by a workspace owner (Delete records to delete):
| Call | Body |
|---|---|
POST /v1/proposal-clauses |
title (up to 160), text (up to 10,000), optional bold, is_default (added to every new proposal; needs wording). Returns 201 with the clause. |
PATCH /v1/proposal-clauses/{id} |
Any of the same fields. |
DELETE /v1/proposal-clauses/{id} |
Removes it (204). |
Proposals that already include a clause keep their own copy when it changes or is removed.