Skip to main content

πŸ“„ PDF Attachment - Complete Usage Guide

Purpose: How to generate and attach PDFs to communications
Available Templates: Invoice, Payment, Order, General
Use Cases: Email invoices, payment receipts, order confirmations, custom documents


🎯 THREE WAYS TO USE PDF ATTACHMENTS​

  1. Via REST API (from frontend or external apps)
  2. Via Backend Service (from other backend services)
  3. Auto-Attach When Sending (integrated into send flow)

Endpoint:​

POST /communications/:id/attach-pdf

Request Body:​

{
templateType: "invoice" | "payment" | "order" | "general",
templateData: { ... }, // Template-specific data
autoAttach?: boolean // If true, includes in next send
}

Example 1: Attach Invoice PDF​

curl -X POST http://localhost:3000/communications/comm-123/attach-pdf \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-001",
"invoiceDate": "2025-11-01",
"dueDate": "2025-11-15",
"companyName": "FlowPOS",
"companyAddress": "123 Main St, City, State 12345",
"companyPhone": "+1234567890",
"companyEmail": "info@flowpos.com",
"customerName": "John Doe",
"customerEmail": "john@example.com",
"items": [
{
"name": "Product A",
"quantity": 2,
"unitPrice": "$100.00",
"amount": "$200.00"
},
{
"name": "Product B",
"quantity": 3,
"unitPrice": "$100.00",
"amount": "$300.00"
}
],
"subtotal": "$500.00",
"tax": "$50.00",
"totalAmount": "$550.00",
"paymentTerms": "Net 30",
"notes": "Thank you for your business!",
"paymentInstructions": "Please pay via bank transfer or credit card"
},
"autoAttach": true
}'

Response:

{
"success": true,
"pdfFilename": "invoice-2025-11-01-comm-123.pdf",
"pdfSize": 45678,
"communication": { ... }
}

Example 2: Attach Payment PDF​

curl -X POST http://localhost:3000/communications/comm-456/attach-pdf \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"templateType": "payment",
"templateData": {
"paymentId": "PAY-001",
"paymentDate": "2025-11-01",
"customerName": "Jane Smith",
"paymentMethod": "Credit Card",
"amount": "$550.00",
"companyName": "FlowPOS",
"transactionId": "TXN-ABC123",
"invoiceNumber": "INV-001",
"notes": "Payment received successfully",
"description": "Thank you for your prompt payment!"
}
}'

Example 3: Attach Order PDF​

curl -X POST http://localhost:3000/communications/comm-789/attach-pdf \
-H "Content-Type: application/json" \
-d '{
"templateType": "order",
"templateData": {
"orderNumber": "ORD-001",
"orderDate": "2025-11-01",
"customerName": "Bob Johnson",
"customerEmail": "bob@example.com",
"customerPhone": "+1234567890",
"companyName": "FlowPOS",
"items": [
{
"name": "Widget A",
"description": "High-quality widget",
"quantity": 5,
"price": "$20.00",
"total": "$100.00"
}
],
"subtotal": "$100.00",
"shippingCost": "$10.00",
"tax": "$11.00",
"totalAmount": "$121.00",
"shippingAddress": "456 Oak Ave, City, State 67890",
"estimatedDelivery": "November 5, 2025",
"trackingNumber": "TRACK123456",
"trackingUrl": "https://tracking.example.com/TRACK123456"
}
}'

Example 4: Attach General PDF​

curl -X POST http://localhost:3000/communications/comm-999/attach-pdf \
-H "Content-Type: application/json" \
-d '{
"templateType": "general",
"templateData": {
"companyName": "FlowPOS",
"title": "Important Announcement",
"subtitle": "Q4 2025 Update",
"recipientName": "Valued Customer",
"greeting": "Dear Valued Customer,",
"message": "<p>We are excited to announce new features coming in Q4 2025!</p><p>These include improved reporting, mobile app, and more.</p>",
"highlightText": "Early access available for premium customers",
"actionUrl": "https://flowpos.com/early-access",
"actionText": "Sign Up for Early Access",
"details": [
{ "label": "Release Date", "value": "December 1, 2025" },
{ "label": "Pricing", "value": "No additional cost" },
{ "label": "Support", "value": "24/7 available" }
],
"notes": "Contact us if you have any questions",
"companyEmail": "support@flowpos.com"
}
}'

πŸ’» METHOD 2: BACKEND SERVICE (From Your Code)​

In Your NestJS Service:​

import { CommunicationPdfService } from '@/communications/application/services/communication-pdf.service';
import { AttachPdfToCommunicationUseCase } from '@/communications/application/use-cases/attach-pdf-to-communication.use-case';

@Injectable()
export class YourService {
constructor(
private readonly pdfService: CommunicationPdfService,
private readonly attachPdfUseCase: AttachPdfToCommunicationUseCase,
) {}

async sendInvoiceWithPdf(invoice: Invoice, customerId: string) {
// 1. Create communication
const communication = await this.communicationsService.send({
businessId: invoice.businessId,
recipientType: 'customer',
recipientId: customerId,
recipientContact: invoice.customerEmail,
channel: 'email',
type: 'invoice',
subject: `Invoice ${invoice.number}`,
content: `Your invoice ${invoice.number} is attached.`,
});

// 2. Generate and attach PDF
const pdfResult = await this.attachPdfUseCase.execute({
communicationId: communication.id,
templateType: 'invoice',
templateData: {
invoiceNumber: invoice.number,
invoiceDate: invoice.date,
dueDate: invoice.dueDate,
companyName: invoice.business.name,
companyAddress: invoice.business.address,
customerName: invoice.customer.name,
customerEmail: invoice.customer.email,
items: invoice.items.map(item => ({
name: item.product.name,
quantity: item.quantity,
unitPrice: `$${item.unitPrice.toFixed(2)}`,
amount: `$${item.total.toFixed(2)}`
})),
subtotal: `$${invoice.subtotal.toFixed(2)}`,
tax: `$${invoice.tax.toFixed(2)}`,
totalAmount: `$${invoice.total.toFixed(2)}`,
paymentTerms: 'Net 30',
},
autoAttach: true,
});

console.log(`PDF attached: ${pdfResult.pdfFilename}`);
return pdfResult;
}
}

Direct PDF Generation (Without Communication):​

@Injectable()
export class InvoiceService {
constructor(private readonly pdfService: CommunicationPdfService) {}

async generateInvoicePdfOnly(invoice: Invoice) {
// Create a mock communication object (just for PDF generation)
const mockCommunication = {
id: 'temp-' + Date.now(),
channel: 'email',
type: 'invoice',
// ... other required fields
};

// Generate PDF
const pdf = await this.pdfService.generateInvoicePdf(
mockCommunication as any,
{
invoiceNumber: invoice.number,
customerName: invoice.customer.name,
totalAmount: `$${invoice.total.toFixed(2)}`,
// ... all other data
}
);

// pdf = { filename, content (base64), mimeType }

// You can now:
// - Save to file system
// - Return to frontend for download
// - Email directly
// - Store in database

return pdf;
}
}

πŸ”„ METHOD 3: AUTO-ATTACH DURING SEND (Integrated)​

Future Enhancement:​

You can modify the CommunicationsService.send() method to automatically generate and attach PDFs based on communication type:

// In communications.service.ts

async send(data: SendCommunicationDto): Promise<SelectableCommunication> {
// ... existing send logic ...

// Auto-generate PDF for invoice emails
if (data.type === 'invoice' && data.channel === 'email' && data.invoiceData) {
const pdf = await this.pdfService.generateInvoicePdf(
communication,
data.invoiceData
);

// Add to attachments
if (!data.attachments) {
data.attachments = [];
}
data.attachments.push({
filename: pdf.filename,
content: pdf.content,
mimeType: pdf.mimeType,
});
}

// Continue with send...
}

πŸ“‹ TEMPLATE DATA REFERENCE​

Invoice Template Data:​

interface InvoiceTemplateData {
// Required
invoiceNumber: string;
invoiceDate: string;
companyName: string;
customerName: string;
totalAmount: string;

// Optional
dueDate?: string;
companyAddress?: string;
companyPhone?: string;
companyEmail?: string;
customerAddress?: string;
customerEmail?: string;
customerPhone?: string;
items?: Array<{
name: string;
quantity: number;
unitPrice: string;
amount: string;
}>;
subtotal?: string;
tax?: string;
discount?: string;
paymentTerms?: string;
referenceNumber?: string;
notes?: string;
paymentInstructions?: string;
footerText?: string;
}

Usage:

await attachPdfUseCase.execute({
communicationId: 'comm-123',
templateType: 'invoice',
templateData: {
invoiceNumber: 'INV-001',
invoiceDate: '2025-11-01',
companyName: 'FlowPOS',
customerName: 'John Doe',
totalAmount: '$550.00',
// ... other optional fields
}
});

Payment Template Data:​

interface PaymentTemplateData {
// Required
paymentId: string;
paymentDate: string;
customerName: string;
paymentMethod: string;
amount: string;
companyName: string;

// Optional
transactionId?: string;
invoiceNumber?: string;
notes?: string;
description?: string;
footerText?: string;
}

Usage:

await attachPdfUseCase.execute({
communicationId: 'comm-456',
templateType: 'payment',
templateData: {
paymentId: 'PAY-001',
paymentDate: '2025-11-01',
customerName: 'Jane Smith',
paymentMethod: 'Credit Card',
amount: '$550.00',
companyName: 'FlowPOS',
transactionId: 'TXN-ABC123',
}
});

Order Template Data:​

interface OrderTemplateData {
// Required
orderNumber: string;
orderDate: string;
customerName: string;
customerEmail: string;
companyName: string;
totalAmount: string;

// Optional
customerPhone?: string;
items?: Array<{
name: string;
description?: string;
quantity: number;
price: string;
total: string;
}>;
subtotal?: string;
shippingCost?: string;
tax?: string;
shippingAddress?: string;
estimatedDelivery?: string;
trackingNumber?: string;
trackingUrl?: string;
notes?: string;
supportEmail?: string;
footerText?: string;
}

Usage:

await attachPdfUseCase.execute({
communicationId: 'comm-789',
templateType: 'order',
templateData: {
orderNumber: 'ORD-001',
orderDate: '2025-11-01',
customerName: 'Bob Johnson',
customerEmail: 'bob@example.com',
companyName: 'FlowPOS',
totalAmount: '$121.00',
items: [
{ name: 'Widget', quantity: 5, price: '$20', total: '$100' }
],
trackingNumber: 'TRACK123456',
}
});

General Template Data:​

interface GeneralTemplateData {
// Required
companyName: string;
message: string; // Can include HTML

// Optional
title?: string;
subtitle?: string;
recipientName?: string;
greeting?: string;
highlightText?: string;
additionalInfo?: string;
actionUrl?: string;
actionText?: string;
details?: Array<{ label: string; value: string }>;
notes?: string;
closing?: string;
senderName?: string;
contactInfo?: string;
logoUrl?: string;
companyAddress?: string;
companyPhone?: string;
companyEmail?: string;
companyWebsite?: string;
footerText?: string;
}

Usage:

await attachPdfUseCase.execute({
communicationId: 'comm-999',
templateType: 'general',
templateData: {
companyName: 'FlowPOS',
title: 'Important Announcement',
message: '<p>We have exciting news to share!</p>',
actionUrl: 'https://flowpos.com/news',
actionText: 'Read More',
}
});

🎨 FROM FRONTEND (TypeScript/React)​

Using the API Service:​

// In your communicationsService.ts (frontend)

export const attachPdfToCommunication = async (
communicationId: string,
templateType: 'invoice' | 'payment' | 'order' | 'general',
templateData: Record<string, unknown>,
autoAttach = true
) => {
const response = await api.post(
`/communications/${communicationId}/attach-pdf`,
{
templateType,
templateData,
autoAttach,
}
);
return response.data;
};

// Usage in your component
const handleAttachInvoice = async (communicationId: string, invoice: Invoice) => {
try {
const result = await attachPdfToCommunication(
communicationId,
'invoice',
{
invoiceNumber: invoice.number,
customerName: invoice.customerName,
totalAmount: `$${invoice.total.toFixed(2)}`,
items: invoice.items.map(item => ({
name: item.name,
quantity: item.quantity,
unitPrice: `$${item.price.toFixed(2)}`,
amount: `$${item.total.toFixed(2)}`,
})),
// ... more fields
}
);

toast.success(`PDF attached: ${result.pdfFilename}`);
} catch (error) {
toast.error('Failed to attach PDF');
}
};

πŸ”§ COMPLETE WORKFLOW EXAMPLES​

Workflow 1: Send Invoice Email with PDF​

// Step 1: Create and send communication
const communication = await communicationsService.send({
businessId: 'business-123',
recipientType: 'customer',
recipientId: 'customer-456',
recipientContact: 'customer@example.com',
channel: 'email',
type: 'invoice',
subject: `Invoice ${invoice.number} from FlowPOS`,
content: `
<p>Dear ${invoice.customerName},</p>
<p>Your invoice is attached to this email.</p>
<p>Total amount: ${invoice.total}</p>
<p>Due date: ${invoice.dueDate}</p>
`,
});

// Step 2: Generate and attach PDF
const pdfResult = await attachPdfUseCase.execute({
communicationId: communication.id,
templateType: 'invoice',
templateData: {
invoiceNumber: invoice.number,
invoiceDate: formatDate(invoice.date),
dueDate: formatDate(invoice.dueDate),
companyName: business.name,
companyAddress: business.address,
customerName: invoice.customer.name,
customerEmail: invoice.customer.email,
items: invoice.items.map(item => ({
name: item.product.name,
quantity: item.quantity,
unitPrice: formatCurrency(item.unitPrice),
amount: formatCurrency(item.total),
})),
subtotal: formatCurrency(invoice.subtotal),
tax: formatCurrency(invoice.tax),
totalAmount: formatCurrency(invoice.total),
paymentTerms: 'Net 30',
notes: 'Thank you for your business!',
},
autoAttach: true,
});

// Step 3: Communication is now sent with PDF attached!
console.log(`Invoice sent with PDF: ${pdfResult.pdfFilename}`);

Workflow 2: Send Payment Confirmation with Receipt​

async function sendPaymentConfirmation(payment: Payment) {
// 1. Create communication
const communication = await communicationsService.send({
businessId: payment.businessId,
recipientType: 'customer',
recipientId: payment.customerId,
recipientContact: payment.customerEmail,
channel: 'email',
type: 'payment_confirmation',
subject: `Payment Confirmation - ${payment.id}`,
content: `
<p>Dear ${payment.customerName},</p>
<p>We have received your payment of ${formatCurrency(payment.amount)}.</p>
<p>Transaction ID: ${payment.transactionId}</p>
<p>Your receipt is attached.</p>
`,
});

// 2. Attach PDF receipt
await attachPdfUseCase.execute({
communicationId: communication.id,
templateType: 'payment',
templateData: {
paymentId: payment.id,
paymentDate: formatDate(payment.date),
customerName: payment.customerName,
paymentMethod: payment.method,
amount: formatCurrency(payment.amount),
companyName: payment.business.name,
transactionId: payment.transactionId,
invoiceNumber: payment.invoiceNumber,
description: 'Thank you for your payment!',
},
autoAttach: true,
});

return communication;
}

Workflow 3: Bulk Attach PDFs​

async function bulkAttachInvoices(communications: SelectableCommunication[], invoices: Invoice[]) {
const requests = communications.map((comm, index) => ({
communicationId: comm.id,
templateType: 'invoice' as const,
templateData: {
invoiceNumber: invoices[index].number,
customerName: invoices[index].customerName,
totalAmount: formatCurrency(invoices[index].total),
// ... more data
},
autoAttach: true,
}));

// Attach all PDFs in bulk
const results = await attachPdfUseCase.executeBulk(requests);

console.log(`Attached ${results.length} PDFs successfully`);
return results;
}

🎯 PRACTICAL USE CASES​

Use Case 1: Invoice Emails​

When: Customer makes a purchase
Action: Send email with invoice PDF attached

// In your sales service
async onSaleCompleted(sale: Sale) {
// Send invoice email
const comm = await communicationsService.send({
recipientContact: sale.customer.email,
channel: 'email',
type: 'invoice',
subject: `Invoice ${sale.invoiceNumber}`,
content: 'Your invoice is attached',
});

// Attach PDF
await attachPdfUseCase.execute({
communicationId: comm.id,
templateType: 'invoice',
templateData: { /* invoice data */ },
autoAttach: true,
});
}

Use Case 2: Payment Receipts​

When: Payment is received
Action: Send confirmation email with receipt PDF

// In your payment service
async onPaymentReceived(payment: Payment) {
const comm = await communicationsService.send({
recipientContact: payment.customer.email,
channel: 'email',
type: 'payment_confirmation',
subject: 'Payment Received',
content: 'Thank you! Receipt attached.',
});

await attachPdfUseCase.execute({
communicationId: comm.id,
templateType: 'payment',
templateData: {
paymentId: payment.id,
amount: formatCurrency(payment.amount),
// ...
},
});
}

Use Case 3: Order Confirmations​

When: Order is placed
Action: Send confirmation email with order details PDF

async onOrderPlaced(order: Order) {
const comm = await communicationsService.send({
recipientContact: order.customer.email,
channel: 'email',
type: 'order_confirmation',
subject: `Order Confirmation #${order.number}`,
content: 'Your order details are attached',
});

await attachPdfUseCase.execute({
communicationId: comm.id,
templateType: 'order',
templateData: {
orderNumber: order.number,
customerName: order.customer.name,
// ...
},
});
}

Use Case 4: Custom Documents​

When: Send any custom document
Action: Use general template for flexibility

async sendCustomDocument(data: {
recipient: string;
title: string;
message: string;
details?: Array<{label: string, value: string}>;
}) {
const comm = await communicationsService.send({
recipientContact: data.recipient,
channel: 'email',
type: 'general_notification',
subject: data.title,
content: data.message,
});

await attachPdfUseCase.execute({
communicationId: comm.id,
templateType: 'general',
templateData: {
companyName: 'FlowPOS',
title: data.title,
message: data.message,
details: data.details,
},
});
}

πŸ” GET ATTACHMENTS​

Get All Attachments for a Communication:​

# Via API
curl http://localhost:3000/communications/comm-123/attachments

Response:

{
"communicationId": "comm-123",
"attachments": [
{
"id": "att-1",
"filename": "invoice-2025-11-01-comm-123.pdf",
"mimeType": "application/pdf",
"sizeBytes": 45678,
"fileUrl": null,
"createdAt": "2025-11-01T10:00:00Z"
}
]
}

From Backend:​

// Get attachments
const attachments = await attachmentHandler.getAttachments('comm-123');

console.log(attachments);
// [
// {
// id: 'att-1',
// filename: 'invoice.pdf',
// mimeType: 'application/pdf',
// sizeBytes: 45678,
// ...
// }
// ]

πŸ“– LIST AVAILABLE TEMPLATES​

Get Available PDF Templates:​

# Via API
curl http://localhost:3000/communications/pdf-templates/available

Response:

[
{
"type": "invoice",
"name": "Invoice",
"description": "Professional invoice with itemized list"
},
{
"type": "payment",
"name": "Payment Confirmation",
"description": "Payment receipt and confirmation"
},
{
"type": "order",
"name": "Order Confirmation",
"description": "Order details with shipping information"
},
{
"type": "general",
"name": "General Communication",
"description": "Flexible template for any communication"
}
]

⚑ QUICK EXAMPLES​

Minimal Invoice:​

await attachPdfUseCase.execute({
communicationId: 'comm-123',
templateType: 'invoice',
templateData: {
invoiceNumber: 'INV-001',
invoiceDate: '2025-11-01',
companyName: 'FlowPOS',
customerName: 'John Doe',
totalAmount: '$100.00',
},
});

Minimal Payment:​

await attachPdfUseCase.execute({
communicationId: 'comm-456',
templateType: 'payment',
templateData: {
paymentId: 'PAY-001',
paymentDate: '2025-11-01',
customerName: 'Jane Smith',
paymentMethod: 'Card',
amount: '$100.00',
companyName: 'FlowPOS',
},
});

Minimal General:​

await attachPdfUseCase.execute({
communicationId: 'comm-999',
templateType: 'general',
templateData: {
companyName: 'FlowPOS',
message: '<p>Important message here</p>',
},
});

πŸ§ͺ TESTING THE FEATURE​

Test with curl:​

# 1. Start backend
cd apps/backend
pnpm start:dev

# 2. First, create a communication
curl -X POST http://localhost:3000/communications/send \
-H "Content-Type: application/json" \
-d '{
"businessId": "business-123",
"recipientType": "customer",
"recipientContact": "test@example.com",
"channel": "email",
"type": "invoice",
"subject": "Test Invoice",
"content": "Testing PDF attachment"
}'

# Note the communication ID from response (e.g., "comm-abc123")

# 3. Attach PDF to that communication
curl -X POST http://localhost:3000/communications/comm-abc123/attach-pdf \
-H "Content-Type: application/json" \
-d '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-TEST-001",
"invoiceDate": "2025-11-01",
"companyName": "FlowPOS Test",
"customerName": "Test Customer",
"totalAmount": "$99.99"
}
}'

# 4. Check attachments
curl http://localhost:3000/communications/comm-abc123/attachments

πŸ’‘ BEST PRACTICES​

1. Always Provide Required Fields​

Each template has required fields. Make sure to include them:

  • Invoice: invoiceNumber, companyName, customerName, totalAmount
  • Payment: paymentId, customerName, amount, companyName
  • Order: orderNumber, customerName, companyName, totalAmount
  • General: companyName, message

2. Format Currency Consistently​

// Good
totalAmount: '$550.00'
unitPrice: '$100.00'

// Also good (with locale)
totalAmount: formatCurrency(550.00) // Returns '$550.00'

3. Format Dates Consistently​

// Good
invoiceDate: '2025-11-01'
invoiceDate: 'November 1, 2025'

// Use helper function
invoiceDate: formatDate(new Date(), 'YYYY-MM-DD')

4. Include Items for Better PDFs​

// Minimal (works)
totalAmount: '$550.00'

// Better (more professional)
items: [
{ name: 'Product A', quantity: 2, unitPrice: '$100', amount: '$200' },
{ name: 'Product B', quantity: 3, unitPrice: '$100', amount: '$300' }
],
subtotal: '$500.00',
tax: '$50.00',
totalAmount: '$550.00'

5. Use autoAttach for Email​

// For emails, use autoAttach: true
await attachPdfUseCase.execute({
communicationId: comm.id,
templateType: 'invoice',
templateData: { ... },
autoAttach: true, // βœ… Automatically includes in email
});

// For non-email channels, autoAttach doesn't apply
// (SMS/WhatsApp can't have PDF attachments)

πŸ› TROUBLESHOOTING​

Error: "Communication not found"​

Cause: Invalid communication ID
Fix: Verify the communication exists

// Check if communication exists
const comm = await communicationsService.findById('comm-123');
if (!comm) {
throw new Error('Communication not found');
}

Error: "Template not found"​

Cause: Template file missing or wrong path
Fix: Check templates exist in:

apps/backend/src/communications/templates/
β”œβ”€β”€ communication-invoice.html
β”œβ”€β”€ communication-payment.html
β”œβ”€β”€ communication-order.html
└── communication-general.html

Missing Data in PDF​

Cause: Template variables not provided
Fix: Provide all required fields

// Check what variables your template needs
// Look at the HTML template file and find {{variables}}

// Invoice requires:
templateData: {
invoiceNumber: '...', // Required
companyName: '...', // Required
customerName: '...', // Required
totalAmount: '...', // Required
// Optional fields can be omitted
}

πŸ“Š MONITORING & DEBUGGING​

Check Logs:​

When you attach a PDF, you'll see logs like:

[CommunicationPdfService] Generating invoice PDF for communication comm-123
[CommunicationPdfService] PDF generated successfully: invoice-2025-11-01-comm-123.pdf (45678 bytes)
[AttachPdfToCommunicationUseCase] Auto-attaching PDF to email communication comm-123
[AttachmentHandlerService] Saved 1 attachment(s) for communication comm-123

Check Database:​

-- See all attachments for a communication
SELECT * FROM communication_attachment
WHERE communication_id = 'comm-123';

-- Results:
-- id | communication_id | filename | mime_type | size_bytes | file_url

🎯 SUMMARY​

Three Ways to Use:​

  1. REST API - From frontend or external apps
  2. Backend Service - From other NestJS services
  3. Integrated - Auto-attach during send

Four Template Types:​

  1. Invoice - Professional invoices with items
  2. Payment - Payment confirmations and receipts
  3. Order - Order confirmations with shipping
  4. General - Flexible for any communication

Key Endpoints:​

POST /communications/:id/attach-pdf     - Attach PDF
GET /communications/:id/attachments - Get attachments
GET /communications/pdf-templates - List templates

βœ… NEXT STEPS​

  1. Try it! Use one of the curl examples above
  2. Integrate Add to your frontend/backend workflows
  3. Customize Modify templates for your branding
  4. Extend Add more template types as needed

Need help with a specific use case? Let me know! πŸš€


Document Version: 1.0
Last Updated: November 1, 2025
Status: βœ… Ready to use!