Proposals
Query Proposal Templates
Section titled “Query Proposal Templates”Returns the published PDF proposal templates of your organization, most recently updated first.
GET https://api.arcsite.com/v1/proposal_templates
curl "https://api.arcsite.com/v1/proposal_templates" \ -H "Authorization: Bearer your_api_token_here"const response = await fetch("https://api.arcsite.com/v1/proposal_templates", { headers: { Authorization: "Bearer your_api_token_here", },});const data = await response.json();import requests
response = requests.get( "https://api.arcsite.com/v1/proposal_templates", headers={"Authorization": "Bearer your_api_token_here"},)data = response.json()[ { "id": "36029621652695041", "name": "Proposal Template 1" }]Query Parameters
Section titled “Query Parameters”| Parameter | Default | In | Description |
|---|---|---|---|
| page | 1 | query | Request a specific page |
| per_page | 10 | query | Page size (maximum 100) |
Query Proposals
Section titled “Query Proposals”Returns a list of proposals of your organization. You can filter by project and paginate; proposals are sorted by most recent activity first. For a change order chain, only the latest version is returned; see Change Orders.
GET https://api.arcsite.com/v1/proposals
curl "https://api.arcsite.com/v1/proposals" \ -H "Authorization: Bearer your_api_token_here"const response = await fetch("https://api.arcsite.com/v1/proposals", { headers: { Authorization: "Bearer your_api_token_here", },});const data = await response.json();import requests
response = requests.get( "https://api.arcsite.com/v1/proposals", headers={"Authorization": "Bearer your_api_token_here"},)data = response.json()[ { "proposal_id": "123", "project_id": "456", "name": "Main Building Proposal", "status": "APPROVED", "sales_representative": "John Doe", "contact_email": "john@example.com", "customer_name": "Jane Smith", "customer_email": "jane@example.com", "document_number": "PROP-2024-001", "template_id": "36029621652695041", "total": 15000.0, "pdf_url": "https://files.example.com/proposals/Drawing_1_Proposal.pdf?Expires=1700000000&Signature=EXAMPLE&Key-Pair-Id=EXAMPLEKEYPAIR", "approved_option": { "id": "789", "drawing_id": "abc123def456", "drawing_version_id": "4441f328-1a1b-4882-bd26-c86ee5a7bb0f" }, "initial_proposal_id": "111" }]Query Parameters
Section titled “Query Parameters”| Parameter | Default | In | Description |
|---|---|---|---|
| project_id | - | query | (optional) Project ID to filter proposals. |
| page | 1 | query | (optional) Request a specific page. |
| per_page | 10 | query | (optional) Page size (maximum 100). |
Proposal
Section titled “Proposal”Each item in the response array is a Proposal object with the following fields. Some fields are returned only for specific statuses.
| Parameter | Type | Description |
|---|---|---|
| proposal_id | String | Proposal ID |
| project_id | String | Project ID the proposal belongs to |
| name | String | Proposal name |
| status | String | Proposal status. See the status values below. |
| sales_representative | String | Name of the sales representative |
| contact_email | String | Email of the sales representative |
| customer_name | String | Customer name |
| customer_email | String | null | Customer email |
| document_number | String | null | Proposal document number |
| template_id | String | null | Proposal template ID; null when the proposal is signed in app |
| close_note | String | (optional) Reason for closing the proposal. Present when status is VOID or LOST. |
| total | Number | (optional) Total amount of the proposal. Present once the proposal is approved: status is APPROVED, PARTIALLY_PAID, PAID or OVER_PAID. For a change order, this is the full revised amount; see Change Orders. |
| pdf_url | String | (optional) URL of the proposal PDF file. Present once the proposal is approved. |
| approved_option | ApprovedOption | (optional) Approved option information. Present once the proposal is approved. |
| initial_proposal_id | String | (optional) ID of the first proposal in the change-order chain. Present on every proposal in a chain, including the original proposal, where it equals proposal_id. |
ApprovedOption
Section titled “ApprovedOption”| Parameter | Type | Description |
|---|---|---|
| id | String | (optional) ID of the approved option, only for online approvals. |
| drawing_id | String | ID of the associated drawing resource. |
| drawing_version_id | String | (optional) ID of the approved drawing version, only for online approvals. |
Get Proposal by ID
Section titled “Get Proposal by ID”Returns one proposal by proposal ID. Returns 404 (Proposal not found) if the proposal doesn’t exist in your organization. The response uses the same base Proposal fields as Query Proposals. For online proposals, the response also includes proposal_options, which can be used to link unsold proposals to the drawing versions they were generated from.
GET https://api.arcsite.com/v1/proposals/<proposal_id>
curl "https://api.arcsite.com/v1/proposals/<proposal_id>" \ -H "Authorization: Bearer your_api_token_here"const response = await fetch("https://api.arcsite.com/v1/proposals/<proposal_id>", { headers: { Authorization: "Bearer your_api_token_here", },});const data = await response.json();import requests
response = requests.get( "https://api.arcsite.com/v1/proposals/<proposal_id>", headers={"Authorization": "Bearer your_api_token_here"},)data = response.json(){ "proposal_id": "123", "project_id": "456", "name": "Main Building Proposal", "status": "PENDING", "sales_representative": "John Doe", "contact_email": "john@example.com", "customer_name": "Jane Smith", "customer_email": "jane@example.com", "document_number": "PROP-2024-001", "template_id": "36029621652695041", "proposal_options": [ { "id": "789", "name": "Option 1", "drawing_id": "abc123def456",6 collapsed lines
"drawing_version_id": "4441f328-1a1b-4882-bd26-c86ee5a7bb0f", "total": 15000.0, "pdf_url": "https://files.example.com/proposals/Drawing_1_Proposal_Option_1.pdf?Expires=1700000000&Signature=EXAMPLE&Key-Pair-Id=EXAMPLEKEYPAIR" } ]}Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
| proposal_id | String | (required) Proposal ID |
Query Parameters
Section titled “Query Parameters”| Parameter | Default | In | Description |
|---|---|---|---|
| include_short_link | false | query | (optional) Set to true to include short_link in the response. |
Response Schema
Section titled “Response Schema”The response is a Proposal object with the fields documented in the Proposal schema. Online proposals also include the following field:
| Parameter | Type | Description |
|---|---|---|
| proposal_options | List[ProposalOption] | (optional) Full list of proposal options for online proposals, including drawing and PDF details |
When include_short_link is true, the response also includes the following field:
| Parameter | Type | Description |
|---|---|---|
| short_link | String | The link the customer opens to view and sign the proposal, the same one as Copy Short Link in ArcSite. null while the proposal is in DRAFT. Doesn’t change once created. |
ProposalOption
Section titled “ProposalOption”| Parameter | Type | Description |
|---|---|---|
| id | String | ID of this proposal option |
| name | String | Proposal option name |
| drawing_id | String | ID of the drawing resource used to generate this option |
| drawing_version_id | String | ID of the drawing version used to generate this option |
| total | Number | Total amount for this proposal option |
| pdf_url | String | Time-limited URL of the proposal option PDF file |
For an approved online proposal, match approved_option.id to proposal_options[].id to identify the approved option. Different options can share a drawing_id, so the drawing ID alone does not identify the approved option.
const approvedOption = data.proposal_options.find( (option) => option.id === data.approved_option.id,);Export Proposal PDF
Section titled “Export Proposal PDF”Generates a proposal PDF for the latest version of a drawing using the given proposal template, and returns a temporary download URL. The PDF is generated synchronously.
POST https://api.arcsite.com/v1/export_proposal_pdf
curl -X POST "https://api.arcsite.com/v1/export_proposal_pdf" \ -H "Authorization: Bearer your_api_token_here" \ -H "Content-Type: application/json" \ -d '{"drawing_id": "your_drawing_id", "template_id": "36029621652695041"}'const response = await fetch("https://api.arcsite.com/v1/export_proposal_pdf", { method: "POST", headers: { Authorization: "Bearer your_api_token_here", "Content-Type": "application/json", }, body: JSON.stringify({ drawing_id: "your_drawing_id", template_id: "36029621652695041", }),});const data = await response.json();import requests
response = requests.post( "https://api.arcsite.com/v1/export_proposal_pdf", headers={"Authorization": "Bearer your_api_token_here"}, json={"drawing_id": "your_drawing_id", "template_id": "36029621652695041"},)data = response.json(){ "url": "https://files.example.com/exports/Drawing_1_Proposal.pdf?X-Amz-Expires=3600&X-Amz-Signature=EXAMPLE"}Request Body
Section titled “Request Body”| Parameter | Type | Description |
|---|---|---|
| drawing_id | String | (required) Drawing id. The PDF is generated from the drawing’s latest version. |
| template_id | String | (optional) Template id from Query proposal templates. Provide exactly one of template_id or template_name. |
| template_name | String | (optional) Exact name of a proposal template. If several templates have this name, the most recently updated one is used. Provide exactly one of template_id or template_name. |
Errors: 400 for an invalid body, or when the drawing is missing required fields or data groups; 403 when the drawing belongs to another organization; 404 when the drawing or template isn’t found.
Get Proposal Payments
Section titled “Get Proposal Payments”Returns the successful and returned payments for the proposal, including payments received on earlier versions of the proposal when it is a change order. Payments are sorted by creation time, oldest first. Failed, pending and fully canceled payments are not included. Returns 404 (Proposal not found) if the proposal doesn’t exist in your organization.
GET https://api.arcsite.com/v1/proposals/<proposal_id>/payments
curl "https://api.arcsite.com/v1/proposals/<proposal_id>/payments" \ -H "Authorization: Bearer your_api_token_here"const response = await fetch("https://api.arcsite.com/v1/proposals/<proposal_id>/payments", { headers: { Authorization: "Bearer your_api_token_here", },});const data = await response.json();import requests
response = requests.get( "https://api.arcsite.com/v1/proposals/<proposal_id>/payments", headers={"Authorization": "Bearer your_api_token_here"},)data = response.json()[ { "paid_amount": 2500.0, "pay_channel": "arcsite_payment", "paid_time": "2025-11-06T10:15:30", "payment_method": "Credit Card", "status": "succeeded", "refunded_amount": "500.00" }, { "paid_amount": 1000.0, "pay_channel": "mark_as_paid", "paid_date": "2025-11-07", "payment_method": "Check", "status": "succeeded" }]Response Schema
Section titled “Response Schema”The response is an array of Payment objects with the following fields:
| Field | Type | Description |
|---|---|---|
| paid_amount | Number | The amount paid |
| pay_channel | String | Channel through which payment was received. Values: arcsite_payment or mark_as_paid |
| paid_time | String | Date and time (UTC) when payment was received, in ISO 8601 format without a timezone designator (e.g. 2025-11-06T10:15:30). Present when pay_channel is arcsite_payment |
| paid_date | String | Date when payment was recorded (YYYY-MM-DD format). Present when pay_channel is mark_as_paid |
| payment_method | String | Method of payment. One of Credit Card, ACH (online payments), or Check, Cash, Wire, Other (marked as paid) |
| status | String | Payment status. Values: succeeded (payment successful) or returned (ACH Return occurred) |
| refunded_amount | String | (optional) Amount refunded, as a decimal string such as "500.00". Only included when greater than zero |
| canceled_amount | String | (optional) Part of this payment that was canceled in ArcSite (no money was moved), as a decimal string such as "200.00". Only included when greater than zero |
Subtract refunded_amount and canceled_amount from paid_amount to get the amount applied to the proposal.
Change Orders
Section titled “Change Orders”A change order revises a proposal that has already been approved. ArcSite creates it as a new proposal in the same chain as the original, and it carries initial_proposal_id, the ID of the first proposal in the chain.
- The
totalof a change order is the full revised contract amount, not the amount added or removed. Use it in place of the earlier version’s total; do not add the two together. - Each new change order replaces the previous version. Query Proposals returns only the latest version in each chain. Earlier versions are still available from Get Proposal by ID.
- A change order has no
totaluntil it is approved. Keep the amount of the last approved version until then. - Get Proposal Payments for a change order includes the payments received on the earlier versions in its chain.