# Export your quote requests with the API

Read your quote requests and custom design submissions, including past ones, into a CRM or spreadsheet with the Sign Customiser API and a Read quotes key.

- Source URL: https://www.signcustomiser.com/help/integrations/17632059-export-your-quote-requests-with-the-api/
- Markdown URL: https://www.signcustomiser.com/help/integrations/17632059-export-your-quote-requests-with-the-api.md
- Category: Integrations
- Last updated: 2026-10-15

## Article

The Sign Customiser API can read the quote requests and custom design submissions that your Store collected, except those that Sign Customiser cannot attribute to your Store (see Limits). Use it to send them to a CRM, a spreadsheet or a report. The API also returns submissions from before you connected your integration. This article is for the person who sets up the integration, usually a developer.

## Before you start

-   You need a Store with quote requests or custom design submissions. They show on the **Quotes** page.

-   You need a tool that can send HTTP requests, for example `curl`, a script or your CRM.

-   The API gives access to customer contact details and uploaded files. Give the key only to the integration that needs them.

## Find the quotes API from your admin

1.  Open the **Quotes** page.

    -   **Universal:** In the left sidebar, under **Manage**, click **Quotes**.

    -   **Shopify:** Open the app from your Shopify admin. On **Home**, in the **Menu** card, click **Quotes**.

2.  Find the **Export your quote requests** card on the right of the page.

3.  To open the API reference, click **View quotes API**.

4.  To open the **Integrations and Webhooks** page, click **Set up export**.

![The Export your quote requests card on the Quotes page, with the View quotes API and Set up export buttons.](/help/intercom-docs/help-centre-images/2c494a0245107a56079791d0d3b87794e6c32bee180d5f4d637d77059c00260c.png)

## Create an API key for quotes

1.  Open **API Tokens**.

    -   **Universal:** In the left sidebar, under **Integrate**, click **API Tokens**.

    -   **Shopify:** On **Home**, in the **Menu** card, click **Tools & Settings**. Then click **API Tokens**.

2.  Click **Create API key**.

3.  Enter a name, for example **CRM quotes sync**.

4.  Under **Access**, clear each checkbox except **Read quotes**. The form opens with all permissions selected.

5.  At the top of the page, click **Create key**.

6.  Click **Copy**. Store the key in your password manager or secrets manager. Sign Customiser shows the complete key one time only.

7.  Click **I've saved this key, close window**.

![The Access section of the Create API key form with the Read quotes checkbox.](/help/intercom-docs/help-centre-images/28a9345d3333f323ba684da51bf3d86886c8c408a4df7a376282c4760b6a70fd.png)

**Read quotes** is the `quotes:read` permission. No other scoped permission reads quotes through the API. A key with the `webhooks:write` permission can still subscribe to the `form:submitted` webhook, which sends each new submission to a URL that you set. Legacy full access keys also give access (see Limits). For more information about keys, read [How to create an API key](../../universal-app/how-to-create-an-api-token/).

## Get your quotes

Send a `GET` request to `https://web.signcustomiser.com/api/v3/quotes`. Send the key as a Bearer token in the `Authorization` header. Do not put the key in the URL.

```
curl "https://web.signcustomiser.com/api/v3/quotes?limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
```

The list shows the newest quote first. It contains quote requests and custom design submissions together. The `type` field of each item shows the kind:

-   `quote`: a quote request.

-   `custom_design`: a custom design form submission.

To get one quote, send a `GET` request to `/api/v3/quotes/{quote_id}`. Use the `id` that the list returns.

## What each quote contains

<table role="presentation" style="border: 1px solid #c6c9c0; border-radius: 6px; border-collapse: separate; border-spacing: 0;"><tbody><tr><td style="padding: 8px;"><p class="no-margin">Field</p></td><td style="border-left: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">What it holds</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>id</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The number that identifies the submission. It never changes.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>type</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>quote</code> or <code>custom_design</code>.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>submission_number</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The count of submissions for one form. Two forms can have the same number.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>customiser_id</code>, <code>form_id</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The customiser and the form that collected the submission.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>responses</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The answers of the customer.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>files</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The files that came with the submission.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>design</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The sign that the customer configured. Only quote requests have it.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>ad_click_ids</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">Ad click IDs, for example <code>gclid</code>. Empty when there are none.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>utm_params</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The UTM values that the customer arrived with. Empty when there are none.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>created_at</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The date and time of the submission, in UTC.</p></td></tr></tbody></table>

### Responses

Each item in `responses` has a `field_id`, a `label`, an `input_type` and a `value`. For a submission that Sign Customiser recorded when the customer sent it, the `label` and `input_type` are the ones that the question had then. If you rename or delete a question later, these submissions do not change. Older submissions that Sign Customiser rebuilt from the form can show the question as it was when the rebuild ran. The `value` is text. It is an empty string (`""`) when the customer left the answer blank or did not tick the box. In some older submissions a blank answer is `null`. Treat `null` and an empty string as the same blank answer. For a multiple-choice answer, the value lists each chosen option on its own line, separated by a line break, not by commas. The `input_type` is one of these values:

-   `text`, `textarea`, `email`, `tel`, `number` or `date`

-   `checkbox`, `select` or `multi_choice`

-   `file`

The `input_type` is `null` for some old submissions. The `label` can also be `null` for them.

### Files

Each item in `files` has a `field_id`, a `file_index`, a `kind`, an `original_filename` and a `url`. The `kind` is one of these:

-   `customer_upload`: a file that the customer uploaded.

-   `product_preview`: an image of the sign that the customer configured.

-   `generated_design`: the SVG that the customiser made, or an image that the customer uploaded for the support background or for a letter part, in its original and cropped copies.

### Design

Quote requests include a `design` object. It holds the options that the customer chose:

-   `text`, `text_align` and `text_orientation`

-   `font`, `colour` and `backlight`

-   `material` and `letter_type`

-   `support` and `support_finish`

-   `jacket`, `mounting` and `mounting_colour`

-   `size_display` and `final_dimensions`

These option values are the labels that the customer saw in the customiser. The `text` value is the text that the customer typed. A value is `null` when the customer made no choice for that option.

The object also holds the price that the customiser quoted:

-   `quoted_price_amount` is a whole number that is 100 times the price. For example, `29900` means 299.00.

-   `quoted_price_display` is the price text that the customer saw.

-   `currency` is the currency of your Store.

This price is the price at the time of the submission. It is not a committed price. A custom design submission has `"design": null`. A quote request with no stored design data also has `"design": null`.

### Example

```
{
  "data": [
    {
      "object": "quote",
      "id": 8123,
      "type": "quote",
      "submission_number": 14,
      "customiser_id": 42,
      "form_id": 7,
      "responses": [
        { "field_id": "3f2a9c1e-5d6b-4a7c-8e9f-0a1b2c3d4e5f", "label": "Email", "input_type": "email", "value": "ada@example.com" }
      ],
      "files": [
        { "field_id": "productImage", "file_index": 0, "kind": "product_preview", "original_filename": "preview.png", "url": "https://example.com/form-files/8Kq2wZ.png" }
      ],
      "design": { "text": "OPEN", "font": "Brush Script", "colour": "Warm White", "quoted_price_amount": 29900, "quoted_price_display": "$299.00", "currency": "AUD" },
      "ad_click_ids": { "gclid": "CjwKCAjw7p6aBhAoBhAAEiwA" },
      "utm_params": { "utm_source": "google", "utm_medium": "cpc" },
      "created_at": "2026-07-19T01:00:00Z"
    }
  ],
  "pagination": { "has_more": false, "next_cursor": null },
  "links": { "self": "https://web.signcustomiser.com/api/v3/quotes", "documentation": "https://www.signcustomiser.com/help/api/", "next": null }
}
```

The example uses made-up values and shortens the `design` object. A real response lists every design field.

## Read every page

Each response has up to `limit` items. The `limit` is from 1 to 100. The default is 20. When `pagination.has_more` is `true`, request the URL in `links.next`. That URL has the cursor. Do not change the cursor. Repeat until `has_more` is `false`.

## Filter the list

Add these query parameters to narrow the list:

-   `filter[type]`: `quote` or `custom_design`.

-   `filter[customiser_id]`: the ID of one customiser.

-   `filter[form_id]`: the ID of one form.

-   `filter[created_from]`: the first day to include, as `YYYY-MM-DD`.

-   `filter[created_to]`: the last day to include, as `YYYY-MM-DD`. It must not be earlier than `filter[created_from]`.

Dates use UTC and include the whole day. If you change a filter, start again from the first page. An old cursor with a new filter causes an `invalid_cursor` error.

## Avoid duplicates

Use `id` to find out if you already saved a quote. Do not use `submission_number`. The `form:submitted` webhook does not send `id`. Because of this, you cannot match a webhook delivery to a quote from the API by identifier.

## Errors

<table role="presentation" style="border: 1px solid #c6c9c0; border-radius: 6px; border-collapse: separate; border-spacing: 0;"><tbody><tr><td style="padding: 8px;"><p class="no-margin">Status</p></td><td style="border-left: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">Code</p></td><td style="border-left: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">What to do</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">401</p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>missing_api_key</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">Send the key as a Bearer token.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">401</p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>invalid_api_key</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">Check the key value, or create a new key.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">401</p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>expired_api_key</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">Create a replacement key.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">403</p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>insufficient_scope</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The key does not have <b>Read quotes</b>. Create a key that has it.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">404</p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>resource_not_found</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">The quote does not exist or does not belong to your Store.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">422</p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>invalid_parameter</code>, <code>unknown_parameter</code>, <code>unknown_filter</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">Correct the query parameter that the response names.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">422</p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>invalid_cursor</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">Start again from the first page, without a cursor.</p></td></tr><tr><td style="border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">429</p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin"><code>rate_limited</code></p></td><td style="border-left: 1px solid #c6c9c0; border-top: 1px solid #c6c9c0; padding: 8px;"><p class="no-margin">Wait the number of seconds in the <code>Retry-After</code> header. Then try again.</p></td></tr></tbody></table>

## Limits

-   The API returns only quotes that Sign Customiser can attribute to your Store. A form can be deleted before the platform recorded form deletions. The list can omit quotes from that form.

-   The quotes endpoints are read-only. You cannot change or delete a quote with them.

-   The file links are public URLs. They can change in the future. Do not treat them as permanent addresses.

-   Older **Legacy full access** keys can read quotes without the **Read quotes** permission. Replace each of them with a scoped key.

## The form:submitted webhook

The `form:submitted` webhook still sends each new submission when it arrives. It is now the legacy way to export quotes. It has no cut-off date. Your existing webhooks continue to work. Use the API for a new integration. For more information, read [Subscribe to Webhooks](../../integrations/subscribe-to-webhooks/).

## Related articles

-   [How to create an API key](../../universal-app/how-to-create-an-api-token/)

-   [Track marketing campaigns with UTM parameters](../../faqs/14631311-track-marketing-campaigns-with-utm-parameters/)

-   [Subscribe to Webhooks](../../integrations/subscribe-to-webhooks/)

-   [Send Google Ads click IDs to your CRM](../../faqs/14190105-send-google-ads-click-ids-to-your-crm/)

-   [Zapier Integration](../../integrations/zapier-integration/)
