Skip to content

Sage One Accounting

Overview

The Sage One Accounting task integrates BaseCloud CRM with Sage One (Sage Business Cloud Accounting), enabling automated creation and retrieval of quotes, tax invoices, and customer records in your Sage accounting system.

Key Features:

  • 6 Actions: Create quotes/invoices, manage customers, fetch documents
  • Basic Authentication: Username/password + company ID
  • Document Number Management: Manual control with automatic sequence reset
  • Item-Based Line Items: Links to Sage inventory items
  • Tax Type Support: Configurable tax rates per line item
  • South African Focus: Optimized for ZAR and SA tax regulations

Use Cases:

  • Sync CRM deals to Sage quotes and invoices
  • Automated customer creation from CRM contacts
  • Invoice generation on service completion
  • Quote workflows with Sage document management
  • Item-based billing with Sage inventory tracking

Prerequisites

1. Sage One Account

  • Active Sage One subscription (South Africa)
  • Account with API access enabled
  • Username and password for authentication
  • Company ID (available in Sage account settings)

2. Sage API Configuration

BaseCloud Server Configuration:

  • API Key must be configured server-side in config/sage.js
  • Contact BaseCloud support to enable Sage integration
  • API Base URL: https://accounting.sageone.co.za/api/2.0.0/

3. Sage Inventory Setup

For Invoice/Quote Creation:

  • Items must exist in Sage inventory with item codes
  • Note exact item codes for use in line items
  • Configure tax types in Sage (note numeric IDs)

4. Customer Records

For Invoice Creation:

  • Customers must exist in Sage with customer IDs
  • Use find_customer or create_customer actions first
  • Note customer IDs for quote/invoice creation

Configuration

Action 1: create_quote

Create a quote in Sage One with line items.

Field Required Description Example
sage_action Yes Must be "create_quote" create_quote
Username Yes Sage One username accounting@company.com
Password Yes Sage One password {{sage_password}} (use Variable)
Company ID Yes Sage company ID 123456
Expiry Date Yes Quote expiry (DD/MM/YYYY HH:mm) 30/03/2024 17:00
Customer ID Yes Sage customer ID (numeric) 78901
Document Number Yes Custom or sequential number QU{{quote_number}}
Reference Yes Reference text CRM Deal #{{deal_id}}
Tax Inclusive Yes "true" or "false" true
Item Code Yes Pipe-delimited Sage item codes CONSULT\|DESIGN\|DEV
Tax Type Yes Pipe-delimited tax type IDs 2\|2\|2
Item Description Yes Pipe-delimited descriptions Consulting\|Design\|Development
Item Comment No Pipe-delimited comments (can be empty) Phase 1\|\|Phase 2
Item Quantity Yes Pipe-delimited quantities 10\|5\|20
Item Unit Price Yes Pipe-delimited prices (incl/excl based on Tax Inclusive) 150.00\|200.00\|100.00

Output Variables:

task_21001_response  // Full Sage API response object
  {
    QuoteID: 123456,
    CustomerId: 78901,
    DocumentNumber: "QU001",
    Reference: "CRM Deal #4567",
    Total: 3750.00,
    TotalIncl: 4312.50,
    Lines: [...],
    Date: "2024-01-15T00:00:00",
    ExpiryDate: "2024-03-30T17:00:00"
  }

How It Works:

  1. Get Current Document Number:
  2. Calls /SystemSettings/GetDocumentNumber?documentTypeId=1 (Quotes)
  3. Stores original DocumentNumber and NextNumber for reset

  4. Lookup Item Selection IDs:

  5. For each Item Code: GET /Item/Get?code={code}&companyid={companyId}
  6. Extracts SelectionId (internal Sage item ID)
  7. Fails if any item code not found

  8. Validate Line Items:

  9. All pipe-delimited arrays must be same length
  10. Exception: Item Comment can be shorter (auto-filled with empty strings)

  11. Build Quote Object:

    {
      ID: 0,  // 0 for new quotes
      Date: today,
      ExpiryDate: provided expiry date (ISO format),
      CustomerId: numeric customer ID,
      DocumentNumber: provided document number,
      Reference: reference text,
      Inclusive: true/false,
      Lines: [
        {
          ID: 0,
          SelectionId: from item lookup,
          TaxTypeId: numeric tax type,
          Description: item description,
          Comments: item comment (or empty),
          Quantity: decimal,
          UnitPriceInclusive: price (if Inclusive=true),
          UnitPriceExclusive: price (if Inclusive=false)
        }
      ]
    }
    

  12. Create Quote:

  13. POST to /Quote/save with Authorization header (Basic Auth)
  14. Returns full quote object with QuoteID

  15. Reset Document Number:

  16. POST /SystemSettings/SetDocumentNumber back to original
  17. Prevents Sage auto-incrementing its own sequence

Error Scenarios:

  • "Failed to get current document number" - Sage API access issue or invalid credentials
  • "Failed to get Item Code {code}" - Item doesn't exist in Sage inventory
  • "Sage line items length mismatch" - Array lengths don't match
  • "Failed to create quote" - Sage API error (check response for details)

Action 2: create_tax_invoice

Create a tax invoice in Sage One. Configuration and process identical to create_quote with these differences:

Field Value/Difference
sage_action create_tax_invoice
Document Type ID 2 (Tax Invoices instead of 1 for Quotes)
API Endpoint /TaxInvoice/save
Due Date Uses today's date (not ExpiryDate)

Output Variables:

task_21001_response  // Full Sage API response
  {
    TaxInvoiceID: 234567,
    CustomerId: 78901,
    DocumentNumber: "INV001",
    Reference: "CRM Deal #4567",
    Total: 3750.00,
    TotalIncl: 4312.50,
    Lines: [...],
    Date: "2024-01-15T00:00:00",
    DueDate: "2024-01-15T00:00:00"
  }

All other fields, validation, and processes same as create_quote.


Action 3: create_customer

Create a new customer in Sage One.

Field Required Description Example
sage_action Yes Must be "create_customer" create_customer
Username Yes Sage One username accounting@company.com
Password Yes Sage One password {{sage_password}}
Company ID Yes Sage company ID 123456
Customer Name Yes Customer name {{client_company_name}}
Customer Email Yes Customer email address {{client_email}}

Output Variables:

task_21001_response  // Full Sage customer object
  {
    CustomerID: 78901,
    Name: "Acme Corporation",
    Email: "billing@acme.com",
    Created: "2024-01-15T10:30:00",
    CompanyId: 123456
  }

How It Works:

  1. POST to /customer/save
  2. Body: { Name: "...", Email: "..." }
  3. Returns complete customer object including CustomerID

Best Practice:

  • Use find_customer first to avoid duplicates
  • Store {{task_21001_response.CustomerID}} in CRM for future use

Action 4: find_customer

Search for existing customer in Sage One by name.

Field Required Description Example
sage_action Yes Must be "find_customer" find_customer
Username Yes Sage One username accounting@company.com
Password Yes Sage One password {{sage_password}}
Company ID Yes Sage company ID 123456
Customer Name Yes Exact customer name {{client_company_name}}

Output Variables:

task_21001_found            // true or false
task_21001_customer_id      // Sage customer ID (if found)
task_21001_customer_name    // Customer name (if found)
task_21001_customer_email   // Customer email (if found)

How It Works:

  1. GET /Customer/Get?name={name}&companyid={companyId}
  2. URL-encodes special characters:
  3. &%26
  4. Space → %20
  5. Other special chars encoded
  6. Returns customer object if exact match found

Search Behavior:

  • Exact match only (case-insensitive)
  • Special characters properly encoded
  • Returns first match if multiple exist

Action 5: fetch_quote

Retrieve existing quote from Sage One by document number.

Field Required Description Example
sage_action Yes Must be "fetch_quote" fetch_quote
Username Yes Sage One username accounting@company.com
Password Yes Sage One password {{sage_password}}
Company ID Yes Sage company ID 123456
Document Number Yes Quote document number QU001

Output Variables:

task_21001_found      // true or false
task_21001_response   // Full Sage quote object (if found)
  {
    QuoteID: 123456,
    DocumentNumber: "QU001",
    CustomerId: 78901,
    Total: 3750.00,
    Lines: [...],
    // ... complete quote details
  }

How It Works:

  1. GET /Quote/Get?documentNumber={number}&companyid={companyId}
  2. Returns full quote object if found
  3. Sets task_21001_found=false if not found

Action 6: fetch_tax_invoice

Retrieve existing tax invoice from Sage One by document number.

Field Required Description Example
sage_action Yes Must be "fetch_tax_invoice" fetch_tax_invoice
Username Yes Sage One username accounting@company.com
Password Yes Sage One password {{sage_password}}
Company ID Yes Sage company ID 123456
Document Number Yes Invoice document number INV001

Output Variables:

task_21001_found      // true or false
task_21001_response   // Full Sage tax invoice object (if found)
  {
    TaxInvoiceID: 234567,
    DocumentNumber: "INV001",
    CustomerId: 78901,
    Total: 4312.50,
    Lines: [...],
    // ... complete invoice details
  }

How It Works:

  1. GET /TaxInvoice/Get?documentNumber={number}&companyid={companyId}
  2. Returns full invoice object if found
  3. Sets task_21001_found=false if not found

Real-World Examples

Example 1: Sync CRM Deal to Sage Quote

Scenario: When deal reaches "Proposal" stage, automatically create quote in Sage with deal line items.

Workflow:

  1. Trigger: CRM Trigger - Deal Stage Changed
  2. Condition: {{deal_stage}} equals "Proposal"

  3. Task: If Condition - Check Customer Exists

    Condition: {{client_sage_customer_id}} is empty
    

  4. Task (Inside If - True): Sage - Find Customer

    sage_action: find_customer
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Customer Name: {{client_company_name}}
    

  5. Task (Inside If - True): If Condition - Customer Not Found

    Condition: {{task_21001_found}} equals false
    

  6. Task (Inside Nested If - True): Sage - Create Customer

    sage_action: create_customer
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Customer Name: {{client_company_name}}
    Customer Email: {{client_email}}
    

  7. Task (Inside Nested If - True): Edit Client - Store Customer ID

    Field: sage_customer_id
    Value: {{task_21001_response.CustomerID}}
    

  8. Task: Variable - Prepare Line Items

    // From deal products:
    item_codes: {{deal_product_codes}}  // Pipe-delimited from CRM
    item_descriptions: {{deal_product_names}}
    item_quantities: {{deal_product_quantities}}
    item_prices: {{deal_product_prices}}
    tax_types: "2|2|2"  // Standard VAT (15%)
    

  9. Task: Sage - Create Quote

    sage_action: create_quote
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Expiry Date: {{deal_expiry_date}}
    Customer ID: {{client_sage_customer_id}}
    Document Number: QU{{deal_id}}
    Reference: Deal #{{deal_id}} - {{deal_name}}
    Tax Inclusive: true
    Item Code: {{item_codes}}
    Tax Type: {{tax_types}}
    Item Description: {{item_descriptions}}
    Item Comment: |||  // Empty comments
    Item Quantity: {{item_quantities}}
    Item Unit Price: {{item_prices}}
    

  10. Task: Edit Client - Store Quote ID

    Field: sage_quote_id
    Value: {{task_21001_response.QuoteID}}
    

  11. Task: Workflow Note - Log Quote Creation

    Note: Sage quote {{task_21001_response.DocumentNumber}} created for deal {{deal_name}}
          Quote ID: {{task_21001_response.QuoteID}}
          Total: R{{task_21001_response.TotalIncl}}
    Type: Finance
    

  12. Task: Email - Notify Sales Team

    To: {{deal_owner_email}}
    Subject: Sage Quote Created - {{client_company_name}}
    Body: Quote {{task_21001_response.DocumentNumber}} has been created in Sage One.
    
          Customer: {{client_company_name}}
          Total: R{{task_21001_response.TotalIncl}}
          Expiry: {{deal_expiry_date}}
    
          Please review in Sage and send to client.
    

Result: CRM deals automatically sync to Sage with customer auto-creation and quote generation, eliminating manual data entry.


Example 2: Quote-to-Invoice Conversion

Scenario: When deal is won, create tax invoice in Sage using same line items as quote.

Workflow:

  1. Trigger: CRM Trigger - Deal Stage Changed
  2. Condition: {{deal_stage}} equals "Won"

  3. Task: Sage - Fetch Quote

    sage_action: fetch_quote
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Document Number: QU{{deal_id}}
    

  4. Task: If Condition - Quote Found

    Condition: {{task_21001_found}} equals true
    

  5. Task (Inside If): Formatter - Extract Line Items

    // Parse {{task_21001_response}} JSON
    Loop through Lines array:
    item_codes: Extract Description → match to item codes
    item_quantities: Extract Quantity
    item_prices: Extract UnitPriceInclusive or UnitPriceExclusive
    item_descriptions: Extract Description
    tax_types: Extract TaxTypeId
    

  6. Task (Inside If): Sage - Create Tax Invoice

    sage_action: create_tax_invoice
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Expiry Date: {{today_date}}  // Due today
    Customer ID: {{task_21001_response.CustomerId}}
    Document Number: INV{{deal_id}}
    Reference: Converted from Quote QU{{deal_id}}
    Tax Inclusive: true
    Item Code: {{extracted_item_codes}}
    Tax Type: {{extracted_tax_types}}
    Item Description: {{extracted_item_descriptions}}
    Item Comment: |||
    Item Quantity: {{extracted_item_quantities}}
    Item Unit Price: {{extracted_item_prices}}
    

  7. Task (Inside If): Edit Client - Store Invoice ID

    Field: sage_invoice_id
    Value: {{task_21001_response.TaxInvoiceID}}
    Field: sage_invoice_number
    Value: {{task_21001_response.DocumentNumber}}
    

  8. Task (Inside If): Workflow Note - Log Conversion

    Note: Quote QU{{deal_id}} converted to Invoice {{task_21001_response.DocumentNumber}}
          Invoice ID: {{task_21001_response.TaxInvoiceID}}
          Amount: R{{task_21001_response.TotalIncl}}
     Type: Finance
    

  9. Task (Inside If): Email - Send Invoice Notification

    To: {{client_email}}
    Cc: accounting@company.com
    Subject: Invoice {{task_21001_response.DocumentNumber}} - {{deal_name}}
    Body: Congratulations on your purchase!
    
          Your invoice is now available in Sage One.
          Invoice Number: {{task_21001_response.DocumentNumber}}
          Amount Due: R{{task_21001_response.TotalIncl}}
    
          Our accounting team will send the formal invoice shortly.
    

Result: Won deals automatically convert to Sage tax invoices, maintaining line item accuracy and enabling immediate billing.


Example 3: Item-Based Service Invoicing

Scenario: Service completion triggers Sage invoice with predefined item codes from Sage inventory.

Workflow:

  1. Trigger: CRM Trigger - Service Status Changed
  2. Condition: {{service_status}} equals "Complete"

  3. Task: MySQL Query - Get Service Details

    SELECT s.service_type, s.quantity, s.sage_item_code,
           i.unit_price, i.tax_type_id, i.description
    FROM services s
    JOIN sage_items i ON s.sage_item_code = i.item_code
    WHERE s.service_id = {{service_id}}
    

  4. Task: Variable - Prepare Invoice Data

    item_code: {{mysql_result_sage_item_code}}
    item_description: {{mysql_result_description}}
    item_quantity: {{mysql_result_quantity}}
    item_price: {{mysql_result_unit_price}}
    tax_type: {{mysql_result_tax_type_id}}
    

  5. Task: Sage - Create Tax Invoice

    sage_action: create_tax_invoice
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Expiry Date: {{service_completion_date}}
    Customer ID: {{client_sage_customer_id}}
    Document Number: INV-SRV-{{service_id}}
    Reference: Service #{{service_id}} - {{service_type}}
    Tax Inclusive: true
    Item Code: {{item_code}}
    Tax Type: {{tax_type}}
    Item Description: {{item_description}}
    Item Comment: Completed {{service_completion_date}}
    Item Quantity: {{item_quantity}}
    Item Unit Price: {{item_price}}
    

  6. Task: MySQL - Update Service Record

    UPDATE services
    SET sage_invoice_id = {{task_21001_response.TaxInvoiceID}},
        sage_invoice_number = '{{task_21001_response.DocumentNumber}}',
        invoiced = 1,
        invoiced_date = NOW()
    WHERE service_id = {{service_id}}
    

  7. Task: Email - Send Completion Notice

    To: {{client_email}}
    Subject: Service Complete - Invoice {{task_21001_response.DocumentNumber}}
    Body: Your service ({{service_type}}) has been completed.
    
          Invoice: {{task_21001_response.DocumentNumber}}
          Amount: R{{task_21001_response.TotalIncl}}
    
          You will receive the formal invoice from our accounting team.
    

  8. Task: Workflow Note - Log Service Invoicing

    Note: Service #{{service_id}} invoiced via Sage
          Invoice: {{task_21001_response.DocumentNumber}}
          Amount: R{{task_21001_response.TotalIncl}}
    Type: Service
    

Result: Service completions automatically generate Sage invoices with correct item codes, maintaining inventory tracking and accurate accounting.


Example 4: Multi-Service Monthly Billing

Scenario: Generate consolidated monthly invoice for client with multiple services, each mapped to Sage item codes.

Workflow:

  1. Trigger: Timer Trigger - Monthly (Last day at 18:00)

  2. Task: MySQL Query - Get Clients with Unbilled Services

    SELECT c.client_id, c.company_name, c.sage_customer_id,
           COUNT(s.service_id) as service_count
    FROM clients c
    JOIN services s ON c.client_id = s.client_id
    WHERE s.invoiced = 0
      AND s.status = 'Complete'
      AND s.completion_date <= LAST_DAY(CURDATE())
    GROUP BY c.client_id
    HAVING service_count > 0
    

  3. Task: Loop - Process Each Client

    Loop through MySQL results
    

  4. Task (Inside Loop): MySQL Query - Get Client Services

    SELECT s.sage_item_code,
           si.description,
           si.unit_price,
           si.tax_type_id,
           COUNT(*) as quantity,
           GROUP_CONCAT(s.service_id SEPARATOR ',') as service_ids
    FROM services s
    JOIN sage_items si ON s.sage_item_code = si.item_code
    WHERE s.client_id = {{loop_item_client_id}}
      AND s.invoiced = 0
      AND s.status = 'Complete'
    GROUP BY s.sage_item_code, si.description, si.unit_price, si.tax_type_id
    

  5. Task (Inside Loop): Formatter - Prepare Line Items

    Format: Pipe-delimited arrays from MySQL grouped results
    item_codes: {{grouped_item_codes}}
    descriptions: {{grouped_descriptions}}
    quantities: {{grouped_quantities}}
    prices: {{grouped_prices}}
    tax_types: {{grouped_tax_types}}
    comments: "Services: {{grouped_service_ids}}|..."
    

  6. Task (Inside Loop): Sage - Create Tax Invoice

    sage_action: create_tax_invoice
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Expiry Date: {{last_day_of_month}}
    Customer ID: {{loop_item_sage_customer_id}}
    Document Number: INV-{{month_code}}-{{loop_item_client_id}}
    Reference: Monthly Services - {{month_name}} {{year}}
    Tax Inclusive: true
    Item Code: {{item_codes}}
    Tax Type: {{tax_types}}
    Item Description: {{descriptions}}
    Item Comment: {{comments}}
    Item Quantity: {{quantities}}
    Item Unit Price: {{prices}}
    

  7. Task (Inside Loop): MySQL - Mark Services Invoiced

    UPDATE services
    SET invoiced = 1,
        sage_invoice_id = {{task_21001_response.TaxInvoiceID}},
        invoiced_date = NOW()
    WHERE client_id = {{loop_item_client_id}}
      AND invoiced = 0
      AND status = 'Complete'
    

  8. Task (Inside Loop): Email - Send Monthly Invoice Notice

    To: {{loop_item_client_email}}
    Subject: Monthly Invoice - {{month_name}} {{year}}
    Body: Your monthly service invoice is ready.
    
          Invoice: {{task_21001_response.DocumentNumber}}
          Services: {{service_count}}
          Total: R{{task_21001_response.TotalIncl}}
    
          Please check your Sage account or contact our accounting team.
    

  9. Task (Inside Loop): Workflow Note - Log Monthly Invoice

    Note: Monthly invoice {{task_21001_response.DocumentNumber}} for {{service_count}} services
          Total: R{{task_21001_response.TotalIncl}}
    Type: Finance
    

Result: Automated monthly billing consolidates multiple services per client into single Sage invoices, reducing manual invoice entry and improving cash flow.


Example 5: Customer Sync Workflow

Scenario: Synchronize CRM clients with Sage customers, updating or creating as needed.

Workflow:

  1. Trigger: Manual Button - Sync to Sage

  2. Task: If Condition - Check Sage Customer ID Exists

    Condition: {{client_sage_customer_id}} is not empty
    

  3. Task (Inside If - True): Workflow Note - Skip Existing

    Note: Client already synced to Sage (Customer ID: {{client_sage_customer_id}})
    Type: System
    

  4. Task (Inside If - False): Sage - Find Customer

    sage_action: find_customer
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Customer Name: {{client_company_name}}
    

  5. Task (Inside If - False): If Condition - Customer Found in Sage

    Condition: {{task_21001_found}} equals true
    

  6. Task (Inside Nested If - True): Edit Client - Link Existing Sage Customer

    Field: sage_customer_id
    Value: {{task_21001_customer_id}}
    

  7. Task (Inside Nested If - True): Workflow Note - Linked Existing

    Note: Linked to existing Sage customer ID {{task_21001_customer_id}}
    Type: System
    

  8. Task (Inside Nested If - False): Sage - Create New Customer

    sage_action: create_customer
    Username: accounting@company.com
    Password: {{sage_password}}
    Company ID: 123456
    Customer Name: {{client_company_name}}
    Customer Email: {{client_email}}
    

  9. Task (Inside Nested If - False): Edit Client - Store New Customer ID

    Field: sage_customer_id
    Value: {{task_21001_response.CustomerID}}
    

  10. Task (Inside Nested If - False): Workflow Note - Created New

    Note: Created new Sage customer ID {{task_21001_response.CustomerID}}
    Type: System
    

  11. Task: Email - Sync Confirmation

    To: {{user_email}}
    Subject: Sage Sync Complete - {{client_company_name}}
    Body: Client successfully synced to Sage One.
    
          Customer ID: {{client_sage_customer_id}}
          Customer Name: {{client_company_name}}
    
          You can now create quotes and invoices for this client.
    

Bulk Sync Variation (Loop):

  1. Trigger: Timer Trigger - Daily (02:00)
  2. Task: MySQL Query - Get Unsynced Clients
  3. Task: Loop - Sync Each Client
  4. Task (Inside Loop): Execute steps 2-10 from above

Result: Automated customer synchronization ensures CRM clients are always available in Sage for invoicing, with duplicate prevention and linking of existing records.


Troubleshooting

Error: "Failed to get current document number"

Cause: Cannot retrieve document number sequence from Sage API

Solutions:

  1. Verify credentials (Username, Password, Company ID) are correct

  2. Check Sage API access is enabled for account

  3. Test credentials manually: Visit https://accounting.sageone.co.za/api/2.0.0/SystemSettings/GetDocumentNumber?documentTypeId=1&companyid={companyId} with Basic Auth

  4. Contact Sage support if API access issues persist

  5. Ensure BaseCloud server has Sage API key configured

Error: "Failed to get Item Code {code}"

Cause: Item code doesn't exist in Sage inventory

Solutions:

  1. Verify item code exists: Log into Sage → Inventory → Items → Search for code

  2. Check exact spelling and case (usually case-insensitive but whitespace matters)

  3. Create missing items in Sage first

  4. Use standardized item codes stored in CRM or database

  5. Validate item codes before invoice creation:

    Loop through item codes → Sage fetch item → If not found, alert user
    

Error: "Sage line items length mismatch"

Cause: Pipe-delimited arrays have different numbers of items

Solutions:

  1. Count pipes in each field - must be equal:

    Item Code: "A|B|C"        // 3 items
    Item Quantity: "1|2|3"    // 3 items
    Item Unit Price: "10|20"  // 2 items ✗ MISMATCH
    

  2. Use Formatter task to validate array lengths before Sage task

  3. Exception: Item Comment can be shorter (auto-filled with empty strings)

  4. Debug: Output arrays to Workflow Note to check lengths

  5. Use consistent data source (e.g., MySQL query with CONCAT/GROUP_CONCAT)

Error: "Failed to create quote/invoice"

Cause: Sage API rejected document creation

Solutions:

  1. Check {{task_21001_response}} for Sage error details

  2. Common issues:

  3. Customer ID doesn't exist (use find_customer first)
  4. Tax Type ID invalid (check Sage tax type configuration)
  5. Document number already exists (use unique numbers)
  6. Date format incorrect (must be DD/MM/YYYY HH:mm)
  7. Negative quantities or prices

  8. Validate all required fields are populated

  9. Test with minimal invoice (1 line item) first

  10. Ensure Tax Inclusive matches price format (inclusive vs exclusive)

Customer Not Found with find_customer

Cause: Exact name match not found in Sage

Solutions:

  1. Check for typos or extra spaces in customer name

  2. Sage search is exact match - "Acme Corp" ≠ "Acme Corporation"

  3. Use standardized naming in CRM to match Sage

  4. Create customer first if not found:

    If {{task_21001_found}} equals false → create_customer action
    

  5. Check special characters are properly handled (&, commas, etc.)

Document Number Sequence Issues

Cause: Manual document number conflicts with Sage auto-increment

Solutions:

  1. Use unique prefixes for CRM-generated documents:
  2. CRM quotes: CRM-QU-001
  3. CRM invoices: CRM-INV-001
  4. Sage internal: QU001, INV001

  5. Query max document number before creation:

    Sage fetch to get latest → Increment by 1 → Use in create
    

  6. Document number reset may fail - check Sage permissions

  7. Consider using Sage's auto-numbering (pass empty string or omit)

Authentication Failures

Cause: Invalid Basic Auth credentials

Solutions:

  1. Verify Username and Password are correct (test in Sage web UI)

  2. Check Company ID is numeric and correct

  3. Use Variable task to securely store credentials (don't hardcode password)

  4. Ensure Sage API key configured on BaseCloud server

  5. Check account has API access enabled (may require premium Sage plan)

  6. Test with curl:

    curl -u "username:password" "https://accounting.sageone.co.za/api/2.0.0/Customer/Get?companyid=123456"
    

Tax Type ID Confusion

Cause: Incorrect or unknown tax type IDs

Solutions:

  1. Tax Type IDs are numeric (e.g., 1, 2, 3) and company-specific

  2. Standard South African VAT: Usually ID 2 (15%)

  3. Find your tax type IDs:

  4. Log into Sage One
  5. Navigate to Settings → Tax Types
  6. Note numeric IDs (may need Sage support for API IDs)

  7. Create reference table in CRM:

    Tax Type 1: Exempt (0%)
    Tax Type 2: Standard VAT (15%)
    Tax Type 3: Zero-rated
    

  8. Use consistent tax types across all line items for simplicity


Best Practices

Credential Management

  1. Secure Storage:
  2. Store Sage password in CRM Variables (encrypted)
  3. Use {{sage_password}} reference in tasks
  4. Never hardcode credentials in automation

  5. Access Control:

  6. Use dedicated Sage user for API access
  7. Limit permissions to necessary operations
  8. Rotate password regularly

  9. Company ID:

  10. Store as CRM setting or owner-level variable
  11. Use {{owner_sage_company_id}} for multi-tenant setups

Item Code Standardization

  1. CRM Item Master:
  2. Maintain item code lookup table in CRM database
  3. Map CRM product IDs to Sage item codes
  4. Include descriptions, prices, tax types

  5. Validation:

  6. Pre-validate item codes exist in Sage before invoice creation
  7. Alert users to missing items early in workflow

  8. Naming Convention:

  9. Use uppercase, short codes (e.g., CONSULT, DESIGN, DEV)
  10. Avoid special characters
  11. Keep codes consistent across CRM and Sage

Customer Synchronization

  1. Proactive Sync:
  2. Create Sage customer when CRM client created (if applicable)
  3. Store sage_customer_id in CRM client record immediately

  4. Duplicate Prevention:

  5. Always use find_customer before create_customer
  6. Check CRM for existing sage_customer_id first

  7. Name Standardization:

  8. Use consistent naming format (e.g., always "Company Name (Pty) Ltd")
  9. Strip special characters that may cause search issues

Document Numbering

  1. Unique Prefixes:
  2. Distinguish CRM-generated docs from Sage internal docs
  3. Example: CRM-INV-{{client_id}}-{{timestamp}}

  4. Sequential Safety:

  5. Don't rely on Sage auto-increment for CRM workflows
  6. Generate unique numbers in CRM (timestamp, UUID, client ID combo)

  7. Reference Field:

  8. Always populate Reference field with CRM context
  9. Example: Deal #{{deal_id}} - {{deal_name}}
  10. Enables easy cross-referencing between systems

Error Handling

  1. Validation Before Creation:
  2. Check customer exists in Sage
  3. Validate all item codes
  4. Verify array lengths match
  5. Test date format

  6. If Conditions:

  7. Wrap Sage actions in error-checking conditions
  8. Provide fallback workflows for failures
  9. Alert users to errors with actionable details

  10. Logging:

  11. Always create Workflow Notes for Sage actions
  12. Include Sage IDs, document numbers, amounts
  13. Log errors with full context for debugging

Performance Optimization

  1. Batch Operations:
  2. Group multiple services into single invoice (see Example 4)
  3. Avoid creating separate invoice per line item

  4. Caching:

  5. Cache item lookups in CRM to reduce Sage API calls
  6. Store frequently-used item Selection IDs

  7. Off-Peak Scheduling:

  8. Schedule bulk operations (monthly billing) during off-peak hours
  9. Avoid simultaneous Sage API calls (use delays in loops)

FAQ

Q: Can I edit Sage quotes/invoices from BaseCloud?

A: No, Sage API doesn't support document updates. Options:

  • Create new document with revised details
  • Manually edit in Sage One web UI
  • Void and recreate via Sage

Q: How do I handle quotes that are never accepted?

A: Sage quotes don't auto-expire:

  • Set up CRM reminder workflow at expiry date
  • Manually void old quotes in Sage
  • Or leave open (doesn't affect accounting until invoiced)

Q: Can I get Sage quote/invoice PDF from BaseCloud?

A: No, Sage API doesn't return PDFs. Workarounds:

  • Access Sage One web UI to download PDF
  • Use Sage's email functionality to send to client
  • Generate parallel PDF in BaseCloud (Type 22 BaseCloud Accounting)

Q: What tax types are available?

A: Tax types are company-specific in Sage. Common South African setup:

  • Type 1: Tax Exempt (0%)
  • Type 2: Standard VAT (15%)
  • Type 3: Zero-Rated (0% but VAT-registered)

Contact Sage support for your specific tax type IDs.

Q: Can I use Sage with currencies other than ZAR?

A: Sage One SA is optimized for ZAR. Multi-currency support limited:

  • Check your Sage plan for multi-currency features
  • May require manual currency handling in Sage
  • Consider currency conversion in CRM before passing to Sage

Q: How do I handle discounts?

A: Sage API doesn't have dedicated discount field. Options:

  1. Discount Line Item: Add negative price line item
  2. Pre-calculate: Subtract discount from unit price before creation
  3. Manual: Apply discount in Sage UI after creation

Q: Can I delete Sage documents from BaseCloud?

A: No, Sage API doesn't support document deletion. Solutions:

  • Void documents in Sage web UI
  • Contact Sage support for deletion assistance
  • Use naming convention to mark obsolete (e.g., "VOID - ")

Q: What happens if document number already exists?

A: Sage API returns error. Solutions:

  • Use unique numbering scheme (timestamp, UUID)
  • Implement collision detection with retry logic
  • Use Sage auto-numbering (but loses CRM reference tracking)

Q: How do I sync Sage invoices back to CRM?

A: Use fetch_tax_invoice or fetch_quote actions:

  1. Timer Trigger: Periodically fetch invoices by document number
  2. Store Sage data in CRM (status, amounts, dates)
  3. Update client records or dashboards

For real-time sync, consider Sage webhooks (if available in your plan).

Q: Can I create credit notes?

A: Sage API doesn't support credit notes via BaseCloud. Options:

  • Create manually in Sage One web UI
  • Create negative invoice as workaround (not ideal)
  • Request credit note API support from Sage

Q: How do I handle partial payments?

A: Sage tracks payment status internally. From BaseCloud:

  • Use fetch_tax_invoice to check payment status
  • Update CRM client record based on Sage status
  • Payment recording happens in Sage (not via API)

Q: What's the API rate limit?

A: Sage One API has rate limits (varies by plan). Best practices:

  • Add delays between bulk operations (use Delay task)
  • Avoid parallel API calls to same Sage account
  • Implement retry logic for rate limit errors (429 status)

Q: Can I create recurring invoices?

A: Not directly via API. Workarounds:

  1. CRM Recurring Workflow:
  2. Timer Trigger: Monthly/weekly
  3. Create invoice via Sage task
  4. Store in CRM for tracking

  5. Sage Internal Recurring:

  6. Set up recurring invoices in Sage One UI
  7. Fetch via API for CRM sync

Q: How do I troubleshoot API errors?

A: Check {{task_21001_response}} for error details:

Response structure:
{
  StatusCode: 400,
  Message: "Descriptive error message",
  ModelState: {
    "Field": ["Validation error details"]
  }
}

Common status codes:

  • 400: Bad request (validation error)
  • 401: Authentication failed
  • 404: Resource not found
  • 500: Sage server error


Technical Details

  • Type ID: 21
  • Function: taskSage() in automationService.js (lines 4446-5028)
  • API: Sage One Accounting API v2.0.0
  • Authentication: HTTP Basic Auth (Username:Password)
  • Base URL: https://accounting.sageone.co.za/api/2.0.0/
  • Output Prefix: task_21001_*
  • Document Types: 1=Quote, 2=Tax Invoice
  • Geographic Focus: South Africa (ZAR, VAT compliance)