Saltar para o conteúdo principal
Automatically send deal data to your invoicing system (Stripe, QuickBooks, Xero, etc.) when an opportunity is won.

Workflow Structure

  1. Trigger: Record is Updated (Opportunity)
  2. Filter: Stage = Closed Won
  3. Search Record: Get Company details
  4. Code (optional): Format payload
  5. HTTP Request: Send to invoicing system

Step 1: Set Up the Trigger

  1. Create a new workflow
  2. Select Record is Updated trigger
  3. Choose Opportunity as the object

Step 2: Filter for Closed Won

Add a Filter action to only continue when the deal is won:
SettingValue
FieldStage
ConditionEquals
ValueCLOSED_WON (or your stage name)
The trigger fires on any Opportunity update. The Filter ensures the workflow only continues when the stage changes to Closed Won.

Step 3: Get Company Details

The Opportunity record may not include all Company fields you need for the invoice. Add a Search Record action:
SettingValue
ObjectCompany
Match byID equals {{trigger.object.companyId}}
This retrieves the full Company record with billing address, tax ID, etc.

Step 4: Format the Payload (Optional)

If your invoicing system expects a specific format, add a Code action:
export const main = async (params: {
  opportunity: any;
  company: any;
}): Promise<object> => {
  const { opportunity, company } = params;

  return {
    invoice: {
      // Customer info from Company
      customer_name: company.name,
      customer_email: company.email || "",
      billing_address: {
        line1: company.address?.street || "",
        city: company.address?.city || "",
        postal_code: company.address?.postalCode || "",
        country: company.address?.country || ""
      },
      tax_id: company.taxId || null,

      // Invoice details from Opportunity
      amount: opportunity.amount,
      currency: opportunity.currency || "USD",
      description: `Invoice for ${opportunity.name}`,
      due_days: 30,

      // Reference back to Twenty
      metadata: {
        opportunity_id: opportunity.id,
        company_id: company.id
      }
    }
  };
};

Step 5: Send to Invoicing System

Add an HTTP Request action:
SettingValue
MethodPOST
URLYour invoicing API endpoint
HeadersAuthorization: Bearer YOUR_API_KEY
Body{{code.invoice}} or map fields directly

Example: Stripe Invoice

POST https://api.stripe.com/v1/invoices
Headers:
  Authorization: Bearer sk_live_xxx
  Content-Type: application/x-www-form-urlencoded

Body:
  customer: {{company.stripeCustomerId}}
  collection_method: send_invoice
  days_until_due: 30

Example: QuickBooks Invoice

POST https://quickbooks.api.intuit.com/v3/company/{realmId}/invoice
Headers:
  Authorization: Bearer YOUR_ACCESS_TOKEN
  Content-Type: application/json

Body: {{code.invoice}}

Complete Workflow Summary

StepActionPurpose
1Trigger: Record UpdatedFires when any Opportunity changes
2FilterOnly proceed if Stage = Closed Won
3Search RecordGet full Company details for billing
4CodeFormat data for invoicing API
5HTTP RequestCreate invoice in external system

Tips

  • Store external IDs: Save the invoice ID returned by the API back to the Opportunity using an Update Record action
  • Error handling: Add a branch to send a notification if the HTTP request fails
  • Test first: Use your invoicing system’s sandbox/test mode before going live