Hey @OBradley5,
Creating an invoice within a HubSpot workflow using custom code is achievable, but there are specific considerations to ensure all desired fields are populated and the invoice is correctly associated with the originating deal.
Your current approach involves two separate API calls: one to create the invoice and another to update its properties. While this method works, it’s more efficient to populate all necessary fields during the initial creation.
Here’s how you can modify your code to include all properties in the initial POST request,
const hubspot = require('@hubspot/api-client');
exports.main = async (event, callback) => {
const hubspotClient = new hubspot.Client({
accessToken: process.env.invoice_creation
});
const dealName = event.inputFields['dealName'];
const amount = event.inputFields['amount'];
const paymentDueDate = event.inputFields['paymentduedate'];
try {
const createInvoice = await hubspotClient.apiRequest({
method: 'POST',
path: '/crm/v3/objects/invoices',
body: {
properties: {
hs_invoice_name: `Invoice for ${dealName}`,
hs_invoice_amount: amount,
hs_invoice_status: 'draft',
hs_invoice_date: new Date().toISOString().split('T')[0],
hs_due_date: paymentDueDate,
hs_payment_terms: 'Due on receipt',
hs_currency: 'GBP'
}
}
});
const invoiceId = createInvoice.body?.id;
if (!invoiceId) throw new Error('Invoice ID not found in response.');
console.log('Invoice successfully created. ID:', invoiceId);
callback({
outputFields: {
invoiceId: invoiceId,
error: ''
}
});
} catch (error) {
console.error('Invoice creation error:', error.message);
callback({
outputFields: {
invoiceId: '',
error: error.message
}
});
}
};
To associate the newly created invoice with the originating deal, you’ll need to use the CRM Associations API.
Example :
const dealId = event.object.objectId;
await hubspotClient.crm.associations.v4.batchApi.create({
inputs: [
{
from: { id: invoiceId },
to: { id: dealId },
types: [
{
associationCategory: 'HUBSPOT_DEFINED',
associationTypeId: 3 // Replace with the correct association type ID
}
]
}
]
});
The associationTypeId should correspond to the correct association between invoices and deals. You can retrieve the appropriate ID using the CRM Association Types API.
Additional : Ensure that your private app has the necessary scopes, such as crm.objects.invoices.write and crm.objects.deals.read, to perform these operations.
Regards,
Kosala