# Quotes

> Create, send, sign, and manage quotes through graph8's quote-to-cash lifecycle

Source: https://docs.graph8.com/developers/quotes/

Manage the full quote-to-cash lifecycle in graph8 — draft quotes with line items, send them for signature, duplicate, and convert accepted or sent quotes back to draft for revision.

> **Caution:**
> `POST /quotes/{quote_id}/send` triggers a real email to the recipient with a signing link. Always verify the recipient and line items before sending.

---

## List Quotes

`GET /quotes`

Returns a paginated list of quotes. All query parameters are optional.

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `status` | string | — | Filter by status: `draft`, `sent`, `viewed`, `accepted`, `declined`, `voided`, or `expired` |
| `mashup_company_id` | integer | — | Filter by company ID |
| `deal_id` | string | — | Filter by deal UUID |
| `owner_id` | string | — | Filter by owner email address |
| `page` | integer | `1` | Page number (1-indexed) |
| `limit` | integer | `50` | Items per page (max 200) |

### Example

**cURL**

```bash
curl "https://be.graph8.com/api/v1/quotes?status=sent&limit=10" \
  -H "Authorization: Bearer $API_KEY"
```

### Response

```json
{
  "data": {
    "items": [
      {
        "id": "quo_abc123",
        "quote_number": "Q-001",
        "title": "Acme Q3 Platform",
        "status": "draft",
        "total": 5000,
        "currency": "USD",
        "created_at": "2026-06-01T10:00:00Z",
        "sent_at": null,
        "accepted_at": null
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}
```

---

## Get Quote

`GET /quotes/{quote_id}`

Returns the full quote record including line items, activity log, and envelope data.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `quote_id` | string | Quote ID |

### Example

**cURL**

```bash
curl "https://be.graph8.com/api/v1/quotes/quo_abc123" \
  -H "Authorization: Bearer $API_KEY"
```

### Response

The response includes all `CBQuote` columns plus:

| Field | Description |
|-------|-------------|
| `line_items` | Array of line item objects |
| `activity` | Array of activity log entries |
| `envelope` | Signing envelope data |

---

## List Quotable Products

`GET /quotable-products`

Returns a paginated list of products that can be used as line items on quotes. All query parameters are optional.

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `currency` | string | — | Filter by 3-character currency code (e.g. `USD`) |
| `page` | integer | `1` | Page number (1-indexed) |
| `limit` | integer | `100` | Items per page (max 500) |

### Example

**cURL**

```bash
curl "https://be.graph8.com/api/v1/quotable-products?currency=USD" \
  -H "Authorization: Bearer $API_KEY"
```

### Response

```json
{
  "data": {
    "items": [{}],
    "total": 0,
    "page": 1,
    "limit": 100
  }
}
```

---

## Get Quote Settings

`GET /quote-settings`

Returns org-level quote settings: branding, default terms, and sender identity. Auto-creates defaults if not yet configured.

### Example

**cURL**

```bash
curl "https://be.graph8.com/api/v1/quote-settings" \
  -H "Authorization: Bearer $API_KEY"
```

---

## Get Quotes by Company

`GET /companies/{company_id}/quotes`

Returns the full quote history for a company as an array of `QuoteDetail` objects.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `company_id` | integer | Company ID |

### Example

**cURL**

```bash
curl "https://be.graph8.com/api/v1/companies/42/quotes" \
  -H "Authorization: Bearer $API_KEY"
```

---

## Get Quotes by Contact

`GET /contacts/{contact_id}/quotes`

Returns all quotes where the contact is the signer, as an array of `QuoteDetail` objects.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `contact_id` | integer | Contact ID |

### Example

**cURL**

```bash
curl "https://be.graph8.com/api/v1/contacts/101/quotes" \
  -H "Authorization: Bearer $API_KEY"
```

---

## Create Quote

`POST /quotes` → `201 Created`

Creates a new draft quote.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | **Yes** | Quote title (1–500 characters) |
| `contract_start_date` | string | **Yes** | Contract start date in ISO format (e.g. `2026-09-01`). Required for every quote. |
| `line_items` | array | **Yes** | Line items (at least 1 required — see Line Items below) |
| `mashup_company_id` | integer | No | Company to associate with the quote |
| `signer_contact_id` | integer | No | Contact ID of the signer. At least one of `signer_contact_id` or `signer_email` is required. |
| `signer_email` | string | No | Signer email address (max 255 characters, must be a valid email). At least one of `signer_contact_id` or `signer_email` is required. |
| `signer_name` | string | No | Signer display name (max 255 characters) |
| `billing_contact_id` | integer | No | Contact ID for billing |
| `billing_legal_name` | string | No | Customer legal entity name shown on the quote (max 255 characters) |
| `billing_primary_email` | string | No | Primary billing contact email (max 255 characters, must be a valid email) |
| `billing_email` | string | No | Secondary billing email (max 255 characters, must be a valid email) |
| `billing_address` | string | No | Billing address (max 2000 characters) |
| `deal_id` | string | No | Deal UUID to associate with the quote |
| `owner_id` | string | No | Quote owner / AE email (max 255 characters). Omit to default to the API caller. |
| `template_id` | string | No | Template UUID |
| `currency` | string | No | 3-character currency code. Default `USD`. |
| `payment_terms` | string | No | One of: `due_on_receipt`, `net_15`, `net_30`, `net_45`, `net_60`, `net_90` |
| `valid_until` | string | No | Quote expiry date in ISO format (e.g. `2026-09-30`) |
| `terms_content` | string | No | Terms and conditions text (max 50 000 characters) |
| `privacy_content` | string | No | Privacy policy text (max 50 000 characters) |
| `notes` | string | No | Internal notes (max 10 000 characters) |
| `public_notes` | string | No | Notes visible to the recipient (max 10 000 characters) |
| `tax_amount` | integer | No | Tax amount in cents (≥ 0) |
| `contract_months` | float | No | Contract length in months (0.1–120) |
| `contract_duration_value` | float | No | Numeric duration value (0.1–3650) |
| `contract_duration_unit` | string | No | Duration unit: `months`, `weeks`, or `days` |
| `contract_end_date` | string | No | Contract end date in ISO format |
| `accepted_payment_methods` | array | No | Accepted payment methods, e.g. `["ach", "credit_card", "wire"]` |
| `marketing_rights` | array | No | Marketing rights granted, e.g. `["logo", "quote", "case_study"]` |

#### Line Items

Each object in `line_items` supports:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `product_name` | string | **Yes** | Product name (1–255 characters) |
| `unit_amount` | integer | **Yes** | Unit price in cents (0–1 000 000 000) |
| `stripe_product_id` | string | No | Stripe product ID. Pass one of `stripe_product_id`, `stripe_price_id`, or `manual_product_id`, or none for a custom line item. |
| `stripe_price_id` | string | No | Stripe price ID |
| `manual_product_id` | string | No | Manual product UUID from `/quotable-products` |
| `description` | string | No | Line item description (max 2000 characters) |
| `quantity` | integer | No | Quantity (1–10 000, default `1`) |
| `discount_pct` | float | No | Discount percentage (0–100, default `0`) |
| `billing_frequency` | string | No | One of: `one_time`, `month`, `year`, `quarter` |
| `sort_order` | integer | No | Display order |

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/quotes" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Acme Q3",
    "contract_start_date": "2026-09-01",
    "signer_contact_id": 101,
    "signer_email": "jane@acme.com",
    "signer_name": "Jane Doe",
    "mashup_company_id": 1,
    "billing_legal_name": "Acme Inc.",
    "billing_primary_email": "ap@acme.com",
    "billing_email": "ar@acme.com",
    "billing_address": "1 Acme Way, San Francisco, CA",
    "deal_id": "deal_uuid",
    "owner_id": "rep@example.com",
    "currency": "USD",
    "payment_terms": "net_30",
    "valid_until": "2026-09-30",
    "tax_amount": 0,
    "contract_months": 12,
    "contract_end_date": "2027-09-01",
    "accepted_payment_methods": ["ach", "credit_card", "wire"],
    "marketing_rights": ["logo", "quote", "case_study"],
    "line_items": [
      {
        "stripe_product_id": "prod_xxx",
        "stripe_price_id": "price_xxx",
        "product_name": "Pro Plan",
        "description": "Monthly subscription",
        "quantity": 1,
        "unit_amount": 50000,
        "discount_pct": 0,
        "billing_frequency": "month",
        "sort_order": 0
      }
    ]
  }'
```

### Validation Errors (`422`)

Invalid input returns `422` with a field-level error list. Common rejections:

| Cause | Error message |
|-------|---------------|
| `contract_start_date` missing or not a valid ISO date | `"contract_start_date is required for quotes."` |
| No signer provided | Must pass at least one of `signer_contact_id` or `signer_email` |
| Invalid email for `signer_email`, `billing_primary_email`, or `billing_email` | Field-level validation error |
| `line_items` is empty or missing | Must be a non-empty list (≥ 1 item) |

---

## Update Quote

`PUT /quotes/{quote_id}` · `PATCH /quotes/{quote_id}`

Both verbs are accepted and behave identically. All fields are optional — only the fields you send are changed.

Draft quotes edit in place. A `sent` or `viewed` quote is recalled to draft first (its live signing link is voided) while keeping the same row and quote number. Terminal quotes (`accepted`, `declined`, `voided`, `expired`) reject the update with `400`.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `quote_id` | string | Quote ID |

### Request Body

All fields are optional. Same fields as [Create Quote](#create-quote).

> **Note:**
> Omit `contract_start_date` to leave the existing value unchanged. Sending it explicitly as empty, non-ISO, or `null` returns `422` — it cannot be cleared on an existing quote.
>
> `owner_id` reassigns the quote owner/AE. A blank value is ignored; an existing owner is never nulled out.
>
> `line_items`, if provided, replaces the full set and re-runs quotable-product authorization.

### Example

**cURL**

```bash
curl -X PATCH "https://be.graph8.com/api/v1/quotes/quo_abc123" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "billing_legal_name": "Acme Corp.",
    "valid_until": "2026-10-31"
  }'
```

---

## Duplicate Quote

`POST /quotes/{quote_id}/duplicate`

Creates a new draft quote by copying the source quote. No request body required.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `quote_id` | string | Quote ID to duplicate |

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/quotes/quo_abc123/duplicate" \
  -H "Authorization: Bearer $API_KEY"
```

### Response

Returns the new draft `QuoteDetail`.

---

## Edit Quote as Draft

`POST /quotes/{quote_id}/edit-as-draft`

Converts a `sent` quote back to a new draft for revision. Voids the existing signing envelope. No request body required.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `quote_id` | string | Quote ID. Must be in `sent` status. |

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/quotes/quo_abc123/edit-as-draft" \
  -H "Authorization: Bearer $API_KEY"
```

### Response

```json
{
  "data": {
    "new_quote_id": "quo_def456",
    "new_quote_number": "Q-002",
    "voided_envelope_id": "env_xyz789",
    "source_quote_id": "quo_abc123"
  }
}
```

---

## Send Quote

`POST /quotes/{quote_id}/send`

Sends the quote to the recipient via email with a signing link. Idempotent for resend.

### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `quote_id` | string | Quote ID |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `subject` | string | No | Email subject line. Falls back to the quote title if omitted. |
| `message` | string | No | Email body message. Falls back to the default cover line if omitted. |

### Example

**cURL**

```bash
curl -X POST "https://be.graph8.com/api/v1/quotes/quo_abc123/send" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "Your Q3 quote from graph8",
    "message": "Hi Jane, please review and sign the attached quote."
  }'
```

### Response

Returns the full `QuoteDetail` after sending.
