Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 3s
146 lines
4.1 KiB
Plaintext
146 lines
4.1 KiB
Plaintext
---
|
|
title: Generate a Quote or Invoice from Twenty
|
|
description: Automatically create invoices in external tools when deals close.
|
|
---
|
|
|
|
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:
|
|
|
|
| Setting | Value |
|
|
|---------|-------|
|
|
| **Field** | Stage |
|
|
| **Condition** | Equals |
|
|
| **Value** | `CLOSED_WON` (or your stage name) |
|
|
|
|
<Tip>
|
|
The trigger fires on any Opportunity update. The Filter ensures the workflow only continues when the stage changes to Closed Won.
|
|
</Tip>
|
|
|
|
## Step 3: Get Company Details
|
|
|
|
The Opportunity record may not include all Company fields you need for the invoice. Add a **Search Record** action:
|
|
|
|
| Setting | Value |
|
|
|---------|-------|
|
|
| **Object** | Company |
|
|
| **Match by** | ID 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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
| Setting | Value |
|
|
|---------|-------|
|
|
| **Method** | POST |
|
|
| **URL** | Your invoicing API endpoint |
|
|
| **Headers** | `Authorization: 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
|
|
|
|
| Step | Action | Purpose |
|
|
|------|--------|---------|
|
|
| 1 | Trigger: Record Updated | Fires when any Opportunity changes |
|
|
| 2 | Filter | Only proceed if Stage = Closed Won |
|
|
| 3 | Search Record | Get full Company details for billing |
|
|
| 4 | Code | Format data for invoicing API |
|
|
| 5 | HTTP Request | Create 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
|
|
|
|
## Related
|
|
|
|
- [Generate a PDF from Twenty](/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty) — attach generated PDFs to records
|
|
- [Workflow Triggers](/user-guide/workflows/capabilities/workflow-triggers)
|
|
- [Workflow Actions](/user-guide/workflows/capabilities/workflow-actions)
|
|
- [Closed Won Automations](/user-guide/workflows/how-tos/crm-automations/closed-won-automations)
|
|
|