Invoices
Invoice generation, approval, lifecycle, and line item operations.
invoices.listInferred outputList invoices for the organization with optional filters.
{
status?: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | undefined;
rootTaskId?: string | undefined;
limit?: number | undefined;
offset?: number | undefined;
contactId?: string | undefined;
projectId?: string | undefined;
issuedAfter?: string | undefined;
issuedBefore?: string | undefined;
dueBefore?: string | undefined;
includeVoid?: boolean | undefined;
}{
data: Array<{
contactName: string | null;
id: string;
status: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | null;
organizationId: string;
rootTaskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
notes: string | null;
billingAddress: string | null;
contactId: string | null;
currency: string | null;
lastSyncedAt: Date | null;
issuedDate: string | null;
subtotal: string | null;
tax: string | null;
total: string | null;
accountingSyncStatus: "not_synced" | "synced" | "modified" | "sync_failed" | "syncing" | null;
accountingSystemId: "xero" | "quickbooks" | "myob" | null;
lastSyncError: string | null;
syncedVersionHash: string | null;
pdfUrl: string | null;
pdfKey: string | null;
pdfGeneratedAt: Date | null;
attentionContactId: string | null;
milestoneId: string | null;
invoiceNumber: string;
dueDate: string | null;
paidDate: string | null;
amountPaid: string | null;
amountDue: string | null;
paymentTerms: string | null;
cachedProjectName: string | null;
cachedProjectCode: string | null;
accountingSystemInvoiceId: string | null;
projectId: string | null;
}>;
total: number;
limit: number;
offset: number;
}invoices.getByIdInferred outputGet a single invoice by ID with optional lines.
{
id: string;
includeLines?: boolean | undefined;
}({
id: string;
status: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | null;
organizationId: string;
rootTaskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
notes: string | null;
billingAddress: string | null;
contactId: string | null;
currency: string | null;
lastSyncedAt: Date | null;
issuedDate: string | null;
subtotal: string | null;
tax: string | null;
total: string | null;
accountingSyncStatus: "not_synced" | "synced" | "modified" | "sync_failed" | "syncing" | null;
accountingSystemId: "xero" | "quickbooks" | "myob" | null;
lastSyncError: string | null;
syncedVersionHash: string | null;
pdfUrl: string | null;
pdfKey: string | null;
pdfGeneratedAt: Date | null;
attentionContactId: string | null;
milestoneId: string | null;
invoiceNumber: string;
dueDate: string | null;
paidDate: string | null;
amountPaid: string | null;
amountDue: string | null;
paymentTerms: string | null;
cachedProjectName: string | null;
cachedProjectCode: string | null;
accountingSystemInvoiceId: string | null;
} & {
projectId: string | null;
}) | {
contact: {
id: string;
organizationId: string;
parentContactId: string | null;
contactType: "company" | "individual";
relationshipType: "client" | "consultant" | "supplier" | "other";
name: string;
email: string | null;
phone: string | null;
jobTitle: string | null;
billingEmail: string | null;
billingAddress: string | null;
paymentTermsDays: number | null;
isBillable: boolean | null;
isActive: boolean | null;
emailBounced: boolean | null;
emailBouncedAt: Date | null;
website: string | null;
taxNumber: string | null;
notes: string | null;
tags: Array<string> | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
} | null;
attentionContact: {
id: string;
organizationId: string;
parentContactId: string | null;
contactType: "company" | "individual";
relationshipType: "client" | "consultant" | "supplier" | "other";
name: string;
email: string | null;
phone: string | null;
jobTitle: string | null;
billingEmail: string | null;
billingAddress: string | null;
paymentTermsDays: number | null;
isBillable: boolean | null;
isActive: boolean | null;
emailBounced: boolean | null;
emailBouncedAt: Date | null;
website: string | null;
taxNumber: string | null;
notes: string | null;
tags: Array<string> | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
} | null;
id: string;
status: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | null;
organizationId: string;
rootTaskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
notes: string | null;
billingAddress: string | null;
contactId: string | null;
currency: string | null;
lastSyncedAt: Date | null;
issuedDate: string | null;
subtotal: string | null;
tax: string | null;
total: string | null;
accountingSyncStatus: "not_synced" | "synced" | "modified" | "sync_failed" | "syncing" | null;
accountingSystemId: "xero" | "quickbooks" | "myob" | null;
lastSyncError: string | null;
syncedVersionHash: string | null;
pdfUrl: string | null;
pdfKey: string | null;
pdfGeneratedAt: Date | null;
attentionContactId: string | null;
milestoneId: string | null;
invoiceNumber: string;
dueDate: string | null;
paidDate: string | null;
amountPaid: string | null;
amountDue: string | null;
paymentTerms: string | null;
cachedProjectName: string | null;
cachedProjectCode: string | null;
accountingSystemInvoiceId: string | null;
projectId: string | null;
} | {
contact: {
id: string;
organizationId: string;
parentContactId: string | null;
contactType: "company" | "individual";
relationshipType: "client" | "consultant" | "supplier" | "other";
name: string;
email: string | null;
phone: string | null;
jobTitle: string | null;
billingEmail: string | null;
billingAddress: string | null;
paymentTermsDays: number | null;
isBillable: boolean | null;
isActive: boolean | null;
emailBounced: boolean | null;
emailBouncedAt: Date | null;
website: string | null;
taxNumber: string | null;
notes: string | null;
tags: Array<string> | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
} | null;
attentionContact: {
id: string;
organizationId: string;
parentContactId: string | null;
contactType: "company" | "individual";
relationshipType: "client" | "consultant" | "supplier" | "other";
name: string;
email: string | null;
phone: string | null;
jobTitle: string | null;
billingEmail: string | null;
billingAddress: string | null;
paymentTermsDays: number | null;
isBillable: boolean | null;
isActive: boolean | null;
emailBounced: boolean | null;
emailBouncedAt: Date | null;
website: string | null;
taxNumber: string | null;
notes: string | null;
tags: Array<string> | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
} | null;
lines: Array<{
id: string;
type: "fixed" | "from_expenses" | "notes" | "progress" | "from_time" | "previously_billed" | "group";
organizationId: string;
taskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
description: string;
hours: string | null;
amount: string;
invoiceId: string;
quantity: string | null;
parentLineId: string | null;
sortOrder: number;
unitValue: string | null;
revenueItemIds: Array<string> | null;
progressPercentage: string | null;
absorbedTimeEntryIds: Array<string> | null;
absorbedExpenseIds: Array<string> | null;
timeFilters: TimeFilters | null;
timeGrouping: TimeGrouping | null;
timeSort: LineSort | null;
timeRateOverrides: Array<TimeRateOverride> | null;
timeRowOverrides: Array<TimeRowOverride> | null;
timeDescriptionTemplate: string | null;
excludedTimeEntryIds: Array<string> | null;
expenseFilters: ExpenseFilters | null;
expenseGrouping: ExpenseGrouping | null;
expenseSort: LineSort | null;
expenseDescriptionTemplate: string | null;
excludedExpenseIds: Array<string> | null;
expenseDescriptionOverrides: Record<string, string> | null;
previousLineIds: Array<string> | null;
isAutoGenerated: boolean;
autoGroupKey: string | null;
}>;
id: string;
status: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | null;
organizationId: string;
rootTaskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
notes: string | null;
billingAddress: string | null;
contactId: string | null;
currency: string | null;
lastSyncedAt: Date | null;
issuedDate: string | null;
subtotal: string | null;
tax: string | null;
total: string | null;
accountingSyncStatus: "not_synced" | "synced" | "modified" | "sync_failed" | "syncing" | null;
accountingSystemId: "xero" | "quickbooks" | "myob" | null;
lastSyncError: string | null;
syncedVersionHash: string | null;
pdfUrl: string | null;
pdfKey: string | null;
pdfGeneratedAt: Date | null;
attentionContactId: string | null;
milestoneId: string | null;
invoiceNumber: string;
dueDate: string | null;
paidDate: string | null;
amountPaid: string | null;
amountDue: string | null;
paymentTerms: string | null;
cachedProjectName: string | null;
cachedProjectCode: string | null;
accountingSystemInvoiceId: string | null;
projectId: string | null;
} | nullinvoices.createInferred outputCreate a new invoice.
{
status?: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | undefined;
rootTaskId?: string | undefined;
notes?: string | undefined;
billingAddress?: string | undefined;
contactId?: string | undefined;
currency?: string | undefined;
projectId?: string | undefined;
issuedDate?: string | undefined;
subtotal?: number | undefined;
tax?: number | undefined;
total?: number | undefined;
attentionContactId?: string | undefined;
milestoneId?: string | undefined;
invoiceNumber?: string | undefined;
dueDate?: string | undefined;
paymentTerms?: string | undefined;
}{
id: string;
status: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | null;
organizationId: string;
rootTaskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
notes: string | null;
billingAddress: string | null;
contactId: string | null;
currency: string | null;
lastSyncedAt: Date | null;
issuedDate: string | null;
subtotal: string | null;
tax: string | null;
total: string | null;
accountingSyncStatus: "not_synced" | "synced" | "modified" | "sync_failed" | "syncing" | null;
accountingSystemId: "xero" | "quickbooks" | "myob" | null;
lastSyncError: string | null;
syncedVersionHash: string | null;
pdfUrl: string | null;
pdfKey: string | null;
pdfGeneratedAt: Date | null;
attentionContactId: string | null;
milestoneId: string | null;
invoiceNumber: string;
dueDate: string | null;
paidDate: string | null;
amountPaid: string | null;
amountDue: string | null;
paymentTerms: string | null;
cachedProjectName: string | null;
cachedProjectCode: string | null;
accountingSystemInvoiceId: string | null;
} & {
projectId: string | null;
}invoices.updateInferred outputUpdate an invoice.
{
id: string;
status?: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | undefined;
rootTaskId?: string | null | undefined;
notes?: string | null | undefined;
billingAddress?: string | null | undefined;
contactId?: string | undefined;
currency?: string | undefined;
projectId?: string | null | undefined;
issuedDate?: string | null | undefined;
subtotal?: number | undefined;
tax?: number | undefined;
total?: number | undefined;
attentionContactId?: string | null | undefined;
milestoneId?: string | null | undefined;
invoiceNumber?: string | undefined;
dueDate?: string | null | undefined;
paidDate?: string | null | undefined;
amountPaid?: number | undefined;
amountDue?: number | undefined;
paymentTerms?: string | null | undefined;
}({
id: string;
status: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | null;
organizationId: string;
rootTaskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
notes: string | null;
billingAddress: string | null;
contactId: string | null;
currency: string | null;
lastSyncedAt: Date | null;
issuedDate: string | null;
subtotal: string | null;
tax: string | null;
total: string | null;
accountingSyncStatus: "not_synced" | "synced" | "modified" | "sync_failed" | "syncing" | null;
accountingSystemId: "xero" | "quickbooks" | "myob" | null;
lastSyncError: string | null;
syncedVersionHash: string | null;
pdfUrl: string | null;
pdfKey: string | null;
pdfGeneratedAt: Date | null;
attentionContactId: string | null;
milestoneId: string | null;
invoiceNumber: string;
dueDate: string | null;
paidDate: string | null;
amountPaid: string | null;
amountDue: string | null;
paymentTerms: string | null;
cachedProjectName: string | null;
cachedProjectCode: string | null;
accountingSystemInvoiceId: string | null;
} & {
projectId: string | null;
}) | nullinvoices.deleteInferred outputSoft delete an invoice.
{
id: string;
}{
success: boolean;
error: string;
} | {
success: boolean;
error?: undefined;
}invoices.addLineInferred outputAdd a line to an invoice.
{
type: "fixed" | "from_expenses" | "notes" | "progress" | "from_time" | "previously_billed" | "group";
invoiceId: string;
taskId?: string | null | undefined;
description?: string | undefined;
quantity?: number | null | undefined;
parentLineId?: string | null | undefined;
sortOrder?: number | undefined;
unitValue?: number | null | undefined;
revenueItemIds?: Array<string> | null | undefined;
progressPercentage?: number | null | undefined;
absorbedTimeEntryIds?: Array<string> | null | undefined;
absorbedExpenseIds?: Array<string> | null | undefined;
timeFilters?: {
dateFrom: string | null;
dateTo: string | null;
poolIds: Array<string> | null;
resourceIds: Array<string> | null;
billableOnly: boolean;
} | null | undefined;
timeGrouping?: "per_pool" | "per_resource" | "per_entry" | "lumped" | null | undefined;
timeSort?: "date_asc" | "date_desc" | "amount_desc" | "label_asc" | null | undefined;
timeRateOverrides?: Array<{
scope: "resource" | "pool" | "entry";
rate: number;
key: string;
}> | null | undefined;
timeRowOverrides?: Array<{
scope: "resource" | "pool" | "entry";
key: string;
description?: string | null | undefined;
hours?: number | null | undefined;
}> | null | undefined;
timeDescriptionTemplate?: string | null | undefined;
excludedTimeEntryIds?: Array<string> | null | undefined;
expenseFilters?: {
dateFrom: string | null;
dateTo: string | null;
supplierIds: Array<string> | null;
categoryIds: Array<string> | null;
contactIds: Array<string> | null;
} | null | undefined;
expenseGrouping?: "lumped" | "per_expense" | null | undefined;
expenseSort?: "date_asc" | "date_desc" | "amount_desc" | "label_asc" | null | undefined;
expenseDescriptionTemplate?: string | null | undefined;
excludedExpenseIds?: Array<string> | null | undefined;
expenseDescriptionOverrides?: Record<string, string> | null | undefined;
previousLineIds?: Array<string> | null | undefined;
}{
id: string;
type: "fixed" | "from_expenses" | "notes" | "progress" | "from_time" | "previously_billed" | "group";
organizationId: string;
taskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
description: string;
hours: string | null;
amount: string;
invoiceId: string;
quantity: string | null;
parentLineId: string | null;
sortOrder: number;
unitValue: string | null;
revenueItemIds: Array<string> | null;
progressPercentage: string | null;
absorbedTimeEntryIds: Array<string> | null;
absorbedExpenseIds: Array<string> | null;
timeFilters: TimeFilters | null;
timeGrouping: TimeGrouping | null;
timeSort: LineSort | null;
timeRateOverrides: Array<TimeRateOverride> | null;
timeRowOverrides: Array<TimeRowOverride> | null;
timeDescriptionTemplate: string | null;
excludedTimeEntryIds: Array<string> | null;
expenseFilters: ExpenseFilters | null;
expenseGrouping: ExpenseGrouping | null;
expenseSort: LineSort | null;
expenseDescriptionTemplate: string | null;
excludedExpenseIds: Array<string> | null;
expenseDescriptionOverrides: Record<string, string> | null;
previousLineIds: Array<string> | null;
isAutoGenerated: boolean;
autoGroupKey: string | null;
}invoices.updateLineInferred outputUpdate an invoice line.
{
id: string;
type?: "fixed" | "from_expenses" | "notes" | "progress" | "from_time" | "previously_billed" | "group" | undefined;
taskId?: string | null | undefined;
description?: string | undefined;
quantity?: number | null | undefined;
parentLineId?: string | null | undefined;
sortOrder?: number | undefined;
unitValue?: number | null | undefined;
revenueItemIds?: Array<string> | null | undefined;
progressPercentage?: number | null | undefined;
absorbedTimeEntryIds?: Array<string> | null | undefined;
absorbedExpenseIds?: Array<string> | null | undefined;
timeFilters?: {
dateFrom: string | null;
dateTo: string | null;
poolIds: Array<string> | null;
resourceIds: Array<string> | null;
billableOnly: boolean;
} | null | undefined;
timeGrouping?: "per_pool" | "per_resource" | "per_entry" | "lumped" | null | undefined;
timeSort?: "date_asc" | "date_desc" | "amount_desc" | "label_asc" | null | undefined;
timeRateOverrides?: Array<{
scope: "resource" | "pool" | "entry";
rate: number;
key: string;
}> | null | undefined;
timeRowOverrides?: Array<{
scope: "resource" | "pool" | "entry";
key: string;
description?: string | null | undefined;
hours?: number | null | undefined;
}> | null | undefined;
timeDescriptionTemplate?: string | null | undefined;
excludedTimeEntryIds?: Array<string> | null | undefined;
expenseFilters?: {
dateFrom: string | null;
dateTo: string | null;
supplierIds: Array<string> | null;
categoryIds: Array<string> | null;
contactIds: Array<string> | null;
} | null | undefined;
expenseGrouping?: "lumped" | "per_expense" | null | undefined;
expenseSort?: "date_asc" | "date_desc" | "amount_desc" | "label_asc" | null | undefined;
expenseDescriptionTemplate?: string | null | undefined;
excludedExpenseIds?: Array<string> | null | undefined;
expenseDescriptionOverrides?: Record<string, string> | null | undefined;
previousLineIds?: Array<string> | null | undefined;
}{
id: string;
type: "fixed" | "from_expenses" | "notes" | "progress" | "from_time" | "previously_billed" | "group";
organizationId: string;
taskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
description: string;
hours: string | null;
amount: string;
invoiceId: string;
quantity: string | null;
parentLineId: string | null;
sortOrder: number;
unitValue: string | null;
revenueItemIds: Array<string> | null;
progressPercentage: string | null;
absorbedTimeEntryIds: Array<string> | null;
absorbedExpenseIds: Array<string> | null;
timeFilters: TimeFilters | null;
timeGrouping: TimeGrouping | null;
timeSort: LineSort | null;
timeRateOverrides: Array<TimeRateOverride> | null;
timeRowOverrides: Array<TimeRowOverride> | null;
timeDescriptionTemplate: string | null;
excludedTimeEntryIds: Array<string> | null;
expenseFilters: ExpenseFilters | null;
expenseGrouping: ExpenseGrouping | null;
expenseSort: LineSort | null;
expenseDescriptionTemplate: string | null;
excludedExpenseIds: Array<string> | null;
expenseDescriptionOverrides: Record<string, string> | null;
previousLineIds: Array<string> | null;
isAutoGenerated: boolean;
autoGroupKey: string | null;
} | nullinvoices.removeLineInferred outputRemove a line from an invoice.
{
id: string;
}{
success: boolean;
error: string;
} | {
success: boolean;
error?: undefined;
}invoices.recalculateTotalsInferred outputRecalculate invoice totals from lines.
{
id: string;
}{
id: string;
status: "draft" | "paid" | "partial" | "sent" | "viewed" | "overdue" | "void" | null;
organizationId: string;
rootTaskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
notes: string | null;
billingAddress: string | null;
contactId: string | null;
currency: string | null;
lastSyncedAt: Date | null;
issuedDate: string | null;
subtotal: string | null;
tax: string | null;
total: string | null;
accountingSyncStatus: "not_synced" | "synced" | "modified" | "sync_failed" | "syncing" | null;
accountingSystemId: "xero" | "quickbooks" | "myob" | null;
lastSyncError: string | null;
syncedVersionHash: string | null;
pdfUrl: string | null;
pdfKey: string | null;
pdfGeneratedAt: Date | null;
attentionContactId: string | null;
milestoneId: string | null;
invoiceNumber: string;
dueDate: string | null;
paidDate: string | null;
amountPaid: string | null;
amountDue: string | null;
paymentTerms: string | null;
cachedProjectName: string | null;
cachedProjectCode: string | null;
accountingSystemInvoiceId: string | null;
} | {
success: boolean;
error: string;
}invoices.generatePdfInferred outputGenerate PDF for an invoice. Triggers async PDF generation via Inngest.
{
invoiceId: string;
regenerate?: boolean | undefined;
}{
success: boolean;
error: string;
message?: undefined;
invoiceId?: undefined;
} | {
success: boolean;
message: string;
invoiceId: string;
error?: undefined;
}invoices.getPdfUrlInferred outputGet PDF URL for an invoice (for polling completion status).
{
invoiceId: string;
}{
ready: boolean;
error: string;
url?: undefined;
generatedAt?: undefined;
} | {
ready: boolean;
url: string | null;
generatedAt: Date | null;
error?: undefined;
}invoices.getPreviousLinesForTaskInferred outputGet previous invoice lines for a task (for "less previously billed" feature). Returns lines from all non-void invoices for this task.
{
taskId: string;
includeChildTasks?: boolean | undefined;
excludeInvoiceId?: string | undefined;
}Array<{
id: string;
type: "fixed" | "from_expenses" | "notes" | "progress" | "from_time" | "previously_billed" | "group";
organizationId: string;
taskId: string | null;
createdAt: Date;
updatedAt: Date | null;
deletedAt: Date | null;
description: string;
hours: string | null;
amount: string;
invoiceId: string;
quantity: string | null;
parentLineId: string | null;
sortOrder: number;
unitValue: string | null;
revenueItemIds: Array<string> | null;
progressPercentage: string | null;
absorbedTimeEntryIds: Array<string> | null;
absorbedExpenseIds: Array<string> | null;
timeFilters: TimeFilters | null;
timeGrouping: TimeGrouping | null;
timeSort: LineSort | null;
timeRateOverrides: Array<TimeRateOverride> | null;
timeRowOverrides: Array<TimeRowOverride> | null;
timeDescriptionTemplate: string | null;
excludedTimeEntryIds: Array<string> | null;
expenseFilters: ExpenseFilters | null;
expenseGrouping: ExpenseGrouping | null;
expenseSort: LineSort | null;
expenseDescriptionTemplate: string | null;
excludedExpenseIds: Array<string> | null;
expenseDescriptionOverrides: Record<string, string> | null;
previousLineIds: Array<string> | null;
isAutoGenerated: boolean;
autoGroupKey: string | null;
} & {
invoiceNumber: string | null;
invoiceStatus: string | null;
issuedDate: string | null;
}>invoices.getTaskFeeForProgressInferred outputGet task fee amount for progress calculation. Returns total task fee and previously billed amount.
{
taskId: string;
}{
taskFeeAmount: number;
previouslyBilledAmount: number;
availableAmount: number;
}invoices.timeEntryCandidatesInferred outputGet time-entry candidates for a From-Time invoice line. Returns entries matching the line's filters with resolved rates and an isExcluded flag (excluded entries are still returned so the panel can grey them out and offer re-inclusion).
{
invoiceId: string;
taskId?: string | undefined;
lineId?: string | undefined;
}Array<TimeEntryCandidate>invoices.expenseCandidatesInferred outputGet expense candidates for a From-Expenses invoice line.
{
invoiceId: string;
taskId?: string | undefined;
lineId?: string | undefined;
}Array<ExpenseCandidate>invoices.previousBillingCandidatesInferred outputGet prior sent invoice lines for a task subtree.
{
taskId: string;
invoiceId: string;
}Array<PreviousBillingCandidate>invoices.sendInvoiceInferred outputSend an invoice via email. If the organization has invoiceEmailApprovalRequired enabled, the email is queued for approval. Otherwise, it's sent immediately.
{
invoiceId: string;
recipientEmail: string;
ccEmails?: Array<string> | undefined;
personalMessage?: string | undefined;
}{
success: boolean;
error: string;
} | {
invoiceId: string;
recipientEmail: string;
queued: boolean;
queueId?: string | undefined;
message: string;
success: boolean;
error?: undefined;
}