Quotes

Use the quotes API to create, manage, and retrieve sales quotes for sharing pricing information with potential buyers. Once configured, a quote can be shared with a buyer either at a specified URL or through PDF. Users can also manage quotes in HubSpot to add details, update associations, and more.

example-quote-api

Example use case: You need to create a contract proposal for a customer who is interested in purchasing one of your annual SEO auditing service packages.

Below, learn how to create a quote through the API and configure its various properties, associations, states, and more.

Please note: the quotes API does not support creating quotes with payment processing through Stripe or HubSpot. You also cannot configure recurring quote-level discounts, fees, or taxes through the API. To use these features, you can instead create the quote in HubSpot

Overview

The quote creation process can be broken up into the following steps:

  1. Create a quote: create a quote with a few details, such as name and expiration date. You can also configure the quote to enable e-signatures
  2. Set up associations: associate the quote with other types of CRM objects, such as line items, a quote template, a deal, and more. During the next step, the quote will inherit property values from some of these associated records as well as the account's settings.
  3. Set the quote state: set the quote's state to reflect its readiness to be shared with buyers. When you set the quote's state, such as making it an editable draft or fully published and publicly accessible quote, it will inherit certain properties from its associated CRM records and account settings.
  4. Share the quote: once a quote has been published, you can share it with your buyers.

Create a quote

To create a quote, you'll first configure its basic details by making a POST request to /crm/v3/objects/quotes. Later, you'll make a separate call to associate the quote with other objects, such as the quote template, a deal, and its line items.

Depending on your preferred workflow, you can instead create a quote with associations through one POST request.

In the post body, include the following required properties to configure its basic details.

hs_title  string (required)

The name of the quote.

hs_expiration_date  string (required)

The date that the quote expires.

The above are just the minimum required properties to get a quote started. To see all available quote properties, make a GET request to crm/v3/properties/quotes. Learn more about the properties API.

// example POST request body { "properties": { "hs_title": "CustomerName - annual SEO audit", "hs_expiration_date": "2023-12-10" } }

The response will include an id, which you'll use to continue configuring the quote. You can update quote properties at any time by making a PATCH request to /crm/v3/objects/quotes/{quoteId}.

Enable e-signatures

To enable e-signatures on the quote, include the hs_esign_enabled boolean property in your request body with a value of true. Note that you will not be able to add countersigners through the API, so those will need to be added in HubSpot before publishing the quote. You also cannot publish a quote with e-sign enabled if you've exceeded your monthly e-signature limit

// example POST request body { "properties": { "hs_title": "CustomerName - annual SEO audit", "hs_expiration_date": "2023-12-10", "hs_esign_enabled": "true" } }

Later, you'll need associate the quote with the quote signers. While the contacts signing the quote exist as contacts in HubSpot, they're stored as a separate association type from contacts. Learn more about associating quotes with quote signers.

Adding associations

To create a complete quote, you'll need to associate it with other CRM records, such as line items, using the associations API. The table below shows which CRM record associations are required for a complete quote, and which are optional. Continue reading to learn more about retrieving IDs and using them to create the needed associations.

Object type Required Description
Line items Yes The goods and/or services being sold through the quote. You can create line items from products in your product library or create custom standalone line items.
Quote template Yes The template that renders the quote, along with providing some default configuration settings for the quote, such as language. Each quote can be associated with one template.
Deal Yes The deal record for tracking revenue and sales lifecycle. A quote inherits values from the associated deal, including the owner and currency. Each quote can be associated with one deal.
Contact No Specific buyers that you're addressing in the quote.
Company No A specific company that you're addressing in the quote. Each quote can be associated with one company.
Discounts, fees, and taxes No Any discounts, fees, and taxes to be applied at checkout. When determining the total quote amount, HubSpot first applies discounts, followed by fees, then taxes. You can use the hs_sort_order field to reorder objects of the same type. Can be set to fixed values or percentages by setting hs_type to either FIXED or PERCENT.

Retrieving IDs for associations

To make each association, you'll first need to retrieve the ID of each object you want to associate. To retrieve each ID, you'll make a GET request to the relevant object endpoint, which follows the same pattern across each CRM object. When making each request, you can also include a properties query parameter to return specific properties when needed. Below are example GET requests for each type of object.

GET request for line items /crm/v3/objects/line_items?properties=name GET request for quote templates /crm/v3/objects/quote_template?properties=hs_name GET request for deals /crm/v3/objects/deals?properties=dealname GET request for contacts /crm/v3/objects/contacts?properties=email GET request for companies /crm/v3/objects/companies?properties=name GET request for discounts crm/v3/objects/discounts?properties=hs_type,hs_value GET request for fees crm/v3/objects/fees?properties=hs_type,hs_value GET request for taxes crm/v3/objects/taxes?properties=hs_type,hs_value #GET request for line items curl --request GET \ --url 'https://api.hubapi.com/crm/v3/properties/line_items?archived=false' \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #GET request for quote templates curl --request GET \ --url 'https://api.hubapi.com/crm/v3/properties/quote_templates?archived=false' \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #GET request for deals curl --request GET \ --url 'https://api.hubapi.com/crm/v3/properties/deals?archived=false' \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #GET request for contacts curl --request GET \ --url 'https://api.hubapi.com/crm/v3/properties/contacts?archived=false' \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #GET request for companies curl --request GET \ --url 'https://api.hubapi.com/crm/v3/properties/companies?archived=false' \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #GET request for discounts curl --request GET \ --url 'https://api.hubapi.com/crm/v3/properties/discounts?archived=false' \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #GET request for fees curl --request GET \ --url 'https://api.hubapi.com/crm/v3/properties/fees?archived=false' \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #GET request for taxes curl --request GET \ --url 'https://api.hubapi.com/crm/v3/properties/taxes?archived=false' \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN'

Each successful call will return a 200 response with details for each fetched object type. You'll use the value in the id field to set associations in the next step.

// Example quote template GET response { "results": [ { "id": "235425923863", "properties": { "hs_createdate": "2023-06-12T16:27:32.794Z", "hs_lastmodifieddate": "2023-06-12T16:27:32.794Z", "hs_name": "Default Basic", "hs_object_id": "235425923863" }, "createdAt": "2023-06-12T16:27:32.794Z", "updatedAt": "2023-06-12T16:27:32.794Z", "archived": false }, { "id": "235425923864", "properties": { "hs_createdate": "2023-06-12T16:27:32.794Z", "hs_lastmodifieddate": "2023-06-12T16:27:32.794Z", "hs_name": "Default Modern", "hs_object_id": "235425923864" }, "createdAt": "2023-06-12T16:27:32.794Z", "updatedAt": "2023-06-12T16:27:32.794Z", "archived": false }, { "id": "235425923865", "properties": { "hs_createdate": "2023-06-12T16:27:32.794Z", "hs_lastmodifieddate": "2023-06-12T16:27:32.794Z", "hs_name": "Default Original", "hs_object_id": "235425923865" }, "createdAt": "2023-06-12T16:27:32.794Z", "updatedAt": "2023-06-12T16:27:32.794Z", "archived": false } ] }

Creating associations

With your IDs retrieved, you can now make calls to the associations API to create associations.

For each type of object you want to associate with a quote, you'll need to make a separate call by making a PUT request using the URL structure below:

 /crm/v4/objects/quotes/{fromObjectId}/associations/default/{toObjectType}/{toObjectId}

Use this table to describe parameters / fields
ParameterDescription
fromObjectId

The ID of the quote.

toObjectType

The type of object you're associating with. For example, line_items, deals, and quote_template

toObjectId

The ID of the object you're associating the quote with. 

Below are example PUT requests for each type of object.

PUT request to associate a line item /crm/v4/objects/quotes/{quoteId}/associations/default/line_items/{lineItemId} PUT request to associate a quote template /crm/v4/objects/quotes/{quoteId}/associations/default/quote_template/{quoteTemplateId} PUT request to associate a deal /crm/v4/objects/quotes/{quoteId}/associations/default/deals/{dealId} PUT request to associate contacts /crm/v4/objects/quotes/{quoteId}/associations/default/contacts/{contactId} PUT request to associate companies /crm/v4/objects/quotes/{quoteId}/associations/default/companies/{companyId} PUT request to associate discounts /crm/v4/objects/quotes/{quoteId}/associations/default/discounts/{discountId} PUT request to associate fees /crm/v4/objects/quotes/{quoteId}/associations/default/fees/{feeId} PUT request to associate taxes /crm/v4/objects/quotes/{quoteId}/associations/default/taxes/{taxId} #PUT request to associate line items curl --request PUT \ --url https://api.hubapi.com/crm/v4/objects/quotes/{quoteId}/associations/default/line_items/{lineItemId} \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #PUT request to associate a quote template curl --request PUT \ --url https://api.hubapi.com/crm/v4/objects/quotes/{quote_ID}/associations/default/quote_template/{quoteTemplateId} \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #PUT request to associate a deal curl --request PUT \ --url https://api.hubapi.com/crm/v4/objects/quotes/{quoteId}/associations/default/deals/{dealId} \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #PUT request to associate contacts curl --request PUT \ --url https://api.hubapi.com/crm/v4/objects/quotes/{quoteId}/associations/default/contacts/{contactId} \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #PUT request to associate a company curl --request PUT \ --url https://api.hubapi.com/crm/v4/objects/quotes/{quoteId}/associations/default/companies/{companyId} \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #PUT request to associate discounts curl --request PUT \ --url https://api.hubapi.com/crm/v4/objects/quotes/{quoteId}/associations/default/discounts/{discountId} \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #PUT request to associate fees curl --request PUT \ --url https://api.hubapi.com/crm/v4/objects/quotes/{quoteId}/associations/default/fees/{feeId} \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN' #PUT request to associate taxes curl --request PUT \ --url https://api.hubapi.com/crm/v4/objects/quotes/{quoteId}/associations/default/line_items/{taxId} \ --header 'authorization: Bearer YOUR_ACCESS_TOKEN'

As an example, if your quote has an ID of 123456, the requests to associate the quote might include the following:

  • Line items (IDs: 55555, 66666): 
    • /crm/v4/objects/quotes/123456/associations/default/line_items/55555
    • /crm/v4/objects/quotes/123456/associations/default/line_items/66666
  • Quote template (ID: 987654):  /crm/v4/objects/quotes/123456/associations/default/quote_template/987654
  • Deal (ID: 345345): /crm/v4/objects/quotes/123456/associations/default/deals/345345

Each successful association will return a 200 response with details about the association. The above calls will associate the objects in both directions, with each direction have its own ID. For example, if you associate the quote with a quote template, the response will describe the association from both ends. In the example response below, 286 is the quote-to-quote-template association type ID, and 285 is the quote-template-to-quote association type ID.

// Example response { "status": "COMPLETE", "results": [ { "from": { "id": "115045534742" }, "to": { "id": "102848290" }, "associationSpec": { "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 285 } }, { "from": { "id": "102848290" }, "to": { "id": "115045534742" }, "associationSpec": { "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 286 } } ], "startedAt": "2023-10-12T16:48:40.624Z", "completedAt": "2023-10-12T16:48:40.712Z" }

Please note: when associating a quote with a quote template, keep in mind the following limits:

  • Quote templates must be created before they can be associated with a quote.
  • A quote can only be associated with on quote template.
  • This API does not support legacy or proposal quotes. Only the CUSTOMIZABLE_QUOTE_TEMPLATE template type can be used.

Associating quote signers

If you're enabling the quote for e-signatures, you'll also need to create an association between the quote and the contacts who are signing by using a specific quote-to-contact association label.

Rather than using the default association endpoints shown above, you'll need to make a PUT request to the following URL:

/crm/v4/objects/quote/{quoteId}/associations/contact/{contactId}

In the request body, you'll need to specify the associationCategory and associationTypeId, as shown below:

// Example request body [ { "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 702 } ]

Create a quote with associations (single request)

The following request body will create a new quote with associations to a quote template, a deal, two line items, and a contact.

POST /crm/v3/objects/quote 

properties  object 

Quote details, which can be retrieved through the properties API. Required properties are: hs_title and hs_expiration_date.

⮑ hs_title  string (required)

The name of the quote.

⮑ hs_expiration_date  string (required)

The date that the quote expires.

⮑ hs_status  string

The quote status. Omitting this property on create will prevent users from being able to edit the quote in HubSpot.

associations  array

The quote's associated records. For a quote to be publishable, it must have an associated deal and quote template. Learn more about association type IDs.

// POST request to https://api.hubapi.com/crm/v3/objects/quote { "properties": { "hs_title": "CustomerName - annual SEO audit", "hs_expiration_date": "2023-09-30" }, "associations": [ { "to": { "id": 115045534742 }, "types": [{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 286 }] }, { "to": { "id": 14795354663 }, "types": [{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 64 }] }, { "to": { "id": 75895447 }, "types": [{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 67 }] }, { "to": { "id": 256143985 }, "types": [{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 67 }] } ] }

Update quote state

A quote's state describes how far along it is in the creation process, from initial set up to being published and publicly accessible. Quote state can also reflect the quote approval process, if quote approvals are enabled for the account. When setting a quote's state, HubSpot will automatically fill in certain properties.

You can update a quote's state by making a PATCH request to /crm/v3/objects/quote/{quoteId}.

A quote's state is based on the hs_status field. Certain quote states allow users to edit, publish, and use the quote in quote approval workflows. Below are the available quote states.

  • No state: if no value is provided for the hs_status field, the quote will be in a Minimal state. The quote will appear on the index page of the quotes tool, but cannot be edited directly. Quotes in this state can still be used in automation, such as the sequences tool, and are also available to analyze within the reporting tool.
  • DRAFT: enables the quote to be edited in HubSpot. This state can be useful for when the quote isn't fully configured or if you'd rather enable sales reps to complete the quote configuration process in HubSpot. 
  • APPROVAL_NOT_NEEDED: publishes the quote at a publicly accessible URL (hs_quote_link) without needing to be approved.
  • PENDING_APPROVAL: indicates that the quote is waiting to be approved before it can be published.
  • APPROVED: the quote has been approved and is now published at a publicly accessible URL (hs_quote_link).
  • REJECTED: indicates that the quote has been set up but has not been approved for publishing, and must be edited before it can be submitted for approval again.

Please note: if you're enabling e-signatures on the quote, you won't be able to publish the quote if you've exceeded your monthly e-signature limit.

For example, the following request would publish the quote at a publicly accessible URL.
// PATCH request to https://api.hubapi.com/crm/v3/objects/quote/{QUOTE_ID} { "properties": { "hs_status": "APPROVAL_NOT_NEEDED" } }

Please note: by default, HubSpot will set the quote's hs_template_type property to CUSTOMIZABLE_QUOTE_TEMPLATE after you update the quote's state. This template type is supported by the v3 API, whereas the following template types are legacy templates that are no longer supported:

  • QUOTE
  • PROPOSAL

Properties set by quote state

Updating the quote's state will update the following properties:

  • hs_quote_amount: calculated based on any associated line items, taxes, discounts, and fees.
  • hs_domain: set from the associated quote template.
  • hs_slug: randomly generated if one is not explicitly provided.
  • hs_quote_total_preference: set based on your account settings. If you haven't configured this setting, it will default to the value of the total_first_payment field.
  • hs_timezone: defaults to your HubSpot account's timezone.
  • hs_locale: set from the associated quote template.
  • hs_quote_number: set based on the current date and time, unless one is provided.
  • hs_language: set from the associated quote template.
  • hs_currency: set based on the associated deal. If you haven't associated a deal with the quote, it will default to your HubSpot account's default currency.

Additionally, the properties below will be calculated when the quote is set to a published state:

  • hs_pdf_download_link: populated with a URL of a PDF for the quote.
  • hs_locked: set to true. To modify any properties after you've published a quote, you must first update the hs_status of the quote back to DRAFT, PENDING_APPROVAL, or REJECTED.
  • hs_quote_link: the quote's publicly accessible URL. This is a read-only property and cannot be set through the API after publishing.
  • hs_esign_num_signers_required: if you've enabled e-signatures, displays the number of signatures required.

Scopes

The following scopes are required for an app to create a valid publishable quote:

  • crm.objects.quotes.write, crm.objects.quotes.read, crm.objects.line_items.write, crm.objects.line_items.read, crm.objects.owners.read, crm.objects.contacts.write, crm.objects.contacts.read, crm.objects.deals.write, crm.objects.deals.read, crm.objects.companies.write, crm.objects.companies.read
  • crm.schemas.quote.read, crm.schemas.line_items.read, crm.schemas.contacts.read, crm.schemas.deals.read, crm.schemas.companies.read

Was this article helpful?
This form is used for documentation feedback only. Learn how to get help with HubSpot.