Skip to content

Instantly share code, notes, and snippets.

@dennisedson
Created March 18, 2025 13:44
Show Gist options
  • Save dennisedson/666742c45701e662075bc44f22902574 to your computer and use it in GitHub Desktop.
Save dennisedson/666742c45701e662075bc44f22902574 to your computer and use it in GitHub Desktop.
Generate an invoice with HubSpot's APIs
const axios = require('axios');
const hubspot = require('@hubspot/api-client');
exports.main = async (event, callback) => {
const token = process.env.Token;
const hubspotClient = new hubspot.Client({ accessToken: token });
// Retrieve the deal record ID (hs_object_id) from the workflow input fields.
const hs_object_id = event.inputFields['hs_object_id']; // This is the deal ID
// Set invoice dates.
const invoiceDate = new Date();
const dueDate = addDays(invoiceDate, 30);
// STEP 1: Create a draft invoice.
const invoiceProperties = {
"hs_invoice_date": invoiceDate.toISOString(),
"hs_due_date": dueDate.toISOString(),
"hs_invoice_status": "draft", // Allowed values: "draft", "open", "paid", "voided"
"hs_currency": "USD"
};
try {
// Create the invoice.
const invoiceResponse = await axios({
method: 'post',
url: 'https://api.hubapi.com/crm/v3/objects/invoices',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
data: JSON.stringify({ properties: invoiceProperties })
});
const invoiceId = invoiceResponse.data.id;
console.log(`Created invoice ${invoiceId}`);
// STEP 2: Associate the invoice with the deal using the default association endpoint.
await axios({
method: 'put',
url: `https://api.hubapi.com/crm/v4/objects/invoices/${invoiceId}/associations/default/deal/${hs_object_id}`,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log(`Associated invoice ${invoiceId} with deal ${hs_object_id}`);
// STEP 3: Retrieve the contact associated with the deal and associate it with the invoice.
const contactsResults = await hubspotClient.crm.associations.v4.basicApi.getPage(
"Deal",
hs_object_id,
"Contact",
undefined,
10
);
if (contactsResults.results.length > 0) {
const contactId = String(contactsResults.results[0].toObjectId);
await axios({
method: 'put',
url: `https://api.hubapi.com/crm/v4/objects/invoices/${invoiceId}/associations/default/contact/${contactId}`,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log(`Associated invoice ${invoiceId} with contact ${contactId}`);
} else {
console.log(`No contact found for deal ${hs_object_id}`);
}
// STEP 3b: Retrieve the company associated with the deal and associate it with the invoice.
const companiesResults = await hubspotClient.crm.associations.v4.basicApi.getPage(
"Deal",
hs_object_id,
"Company",
undefined,
10
);
if (companiesResults.results.length > 0) {
const companyId = String(companiesResults.results[0].toObjectId);
await axios({
method: 'put',
url: `https://api.hubapi.com/crm/v4/objects/invoices/${invoiceId}/associations/default/company/${companyId}`,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log(`Associated invoice ${invoiceId} with company ${companyId}`);
} else {
console.log(`No company found for deal ${hs_object_id}`);
}
// STEP 4: Retrieve the line items associated with the deal.
const lineItemsResults = await hubspotClient.crm.associations.v4.basicApi.getPage(
"Deal",
hs_object_id,
"Line_Item",
undefined,
10
);
const dealLineItemIds = lineItemsResults.results.map(({ toObjectId }) => String(toObjectId));
// STEP 5: For each deal line item, create an equivalent line item and associate it with the invoice.
for (const lineItemId of dealLineItemIds) {
// Retrieve the original line item details.
const lineItemResponse = await axios({
method: 'get',
url: `https://api.hubapi.com/crm/v3/objects/line_items/${lineItemId}`,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const lineItemData = lineItemResponse.data;
// Extract details—adjust property keys as needed.
const { name, quantity, price, hs_product_id, hs_tax_rate_group_id } = lineItemData.properties;
// Build new line item properties.
let newLineItemProperties = {
"name": name,
"quantity": quantity,
"price": price
};
if (hs_product_id) {
newLineItemProperties.hs_product_id = hs_product_id;
}
if (hs_tax_rate_group_id) {
newLineItemProperties.hs_tax_rate_group_id = hs_tax_rate_group_id;
}
// Create the new line item.
const newLineItemResponse = await axios({
method: 'post',
url: 'https://api.hubapi.com/crm/v3/objects/line_items',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
data: JSON.stringify({ properties: newLineItemProperties })
});
const newLineItemId = newLineItemResponse.data.id;
console.log(`Created new line item ${newLineItemId} based on deal line item ${lineItemId}`);
// Associate the new line item with the invoice using the default association endpoint.
await axios({
method: 'put',
url: `https://api.hubapi.com/crm/v4/objects/invoices/${invoiceId}/associations/default/line_item/${newLineItemId}`,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log(`Associated new line item ${newLineItemId} with invoice ${invoiceId}`);
}
// STEP 6: Update the invoice status to "open".
await axios({
method: 'patch',
url: `https://api.hubapi.com/crm/v3/objects/invoices/${invoiceId}`,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
data: JSON.stringify({
properties: {
"hs_invoice_status": "open"
}
})
});
console.log(`Updated invoice ${invoiceId} status to open`);
callback({
outputFields: {
invoiceId: invoiceId
}
});
} catch (error) {
console.error(error.response ? error.response.data : error);
callback({ outputFields: {} });
}
};
// Helper function to add days to a date.
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment