GraphQL Mutations

This document provides a comprehensive reference of all GraphQL mutations available in Crane Ledger's API. Mutations modify data and include create, update, delete, and specialized operations.

Account Mutations

createAccount

Create a new account in the chart of accounts.

mutation CreateAccount($code: String!, $name: String!, $accountType: AccountType!, $description: String) {
  createAccount(
    code: $code
    name: $name
    accountType: $accountType
    description: $description
  ) {
    id
    code
    name
    accountType
    status
    createdAt
  }
}

Parameters:

  • code: String! - Account code (e.g., "1001", "2001")
  • name: String! - Account display name
  • accountType: AccountType! - Account type (ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE)
  • description: String - Optional account description

Return Type: AccountNode

Authentication: Required

Credits: 1

Example:

mutation CreateCashAccount {
  createAccount(
    code: "1001"
    name: "Operating Checking Account"
    accountType: ASSET
    description: "Primary business checking account at Bank of America"
  ) {
    id
    code
    name
    accountType
    status
  }
}

Response:

{
  "data": {
    "createAccount": {
      "id": "act_1234567890abcdef",
      "code": "1001",
      "name": "Operating Checking Account",
      "accountType": "ASSET",
      "status": "ACTIVE"
    }
  }
}

updateAccount

Update an existing account's information.

mutation UpdateAccount(
  $id: ID!
  $code: String
  $name: String
  $description: String
) {
  updateAccount(
    id: $id
    code: $code
    name: $name
    description: $description
  ) {
    id
    code
    name
    description
    updatedAt
  }
}

Parameters:

  • id: ID! - Account ID to update
  • code: String - New account code (optional)
  • name: String - New account name (optional)
  • description: String - New description (optional)

Return Type: AccountNode

Authentication: Required

Credits: 1

Example:

mutation UpdateAccountName {
  updateAccount(
    id: "act_1234567890abcdef"
    name: "Primary Operating Account"
    description: "Updated account description"
  ) {
    id
    name
    description
    updatedAt
  }
}

deleteAccount

Delete an account (only if it has no transactions).

mutation DeleteAccount($id: ID!) {
  deleteAccount(id: $id)
}

Parameters:

  • id: ID! - Account ID to delete

Return Type: Boolean

Authentication: Required

Credits: 1

Example:

mutation RemoveUnusedAccount {
  deleteAccount(id: "act_1234567890abcdef")
}

Response:

{
  "data": {
    "deleteAccount": true
  }
}

Transaction Mutations

createTransaction

Create a new journal entry transaction.

mutation CreateTransaction(
  $description: String!
  $date: DateTime!
  $entries: [LedgerEntryInput!]!
) {
  createTransaction(
    description: $description
    date: $date
    entries: $entries
  ) {
    id
    description
    date
    status
    amount
    entries {
      id
      accountId
      entryType
      amount
      description
    }
    createdAt
  }
}

Parameters:

  • description: String! - Transaction description
  • date: DateTime! - Transaction date
  • entries: [LedgerEntryInput!]! - Journal entries (must balance)

Return Type: TransactionNode

Authentication: Required

Credits: 2

Example:

mutation RecordCashSale {
  createTransaction(
    description: "Cash sale to customer"
    date: "2024-01-15T00:00:00Z"
    entries: [
      {
        accountId: "act_1001"
        entryType: DEBIT
        amount: 250.00
        description: "Cash received"
      }
      {
        accountId: "act_4001"
        entryType: CREDIT
        amount: 250.00
        description: "Revenue earned"
      }
    ]
  ) {
    id
    description
    status
    amount
  }
}

LedgerEntryInput Structure:

input LedgerEntryInput {
  accountId: ID!      # Account to affect
  entryType: EntryType! # DEBIT or CREDIT
  amount: Decimal!    # Entry amount
  description: String # Optional description
  reference: String   # Optional reference
}

Contact Mutations

createContact

Create a new business contact (customer, vendor, etc.).

mutation CreateContact(
  $name: String!
  $contactType: ContactType!
  $email: String
  $phone: String
  $taxId: String
) {
  createContact(
    name: $name
    contactType: $contactType
    email: $email
    phone: $phone
    taxId: $taxId
  ) {
    id
    name
    contactType
    email
    phone
    taxId
    createdAt
  }
}

Parameters:

  • name: String! - Contact full name
  • contactType: ContactType! - Contact type (CUSTOMER, VENDOR, EMPLOYEE, OTHER)
  • email: String - Email address
  • phone: String - Phone number
  • taxId: String - Tax ID or VAT number

Return Type: ContactNode

Authentication: Required

Credits: 1 (worker-queued)

Example:

mutation AddNewCustomer {
  createContact(
    name: "Acme Corporation"
    contactType: CUSTOMER
    email: "billing@acme.com"
    phone: "+1-555-0123"
    taxId: "US123456789"
  ) {
    id
    name
    contactType
    email
  }
}

updateContact

Update an existing contact's information.

mutation UpdateContact(
  $id: ID!
  $name: String
  $email: String
  $phone: String
  $taxId: String
) {
  updateContact(
    id: $id
    name: $name
    email: $email
    phone: $phone
    taxId: $taxId
  ) {
    id
    name
    email
    phone
    taxId
    updatedAt
  }
}

Parameters:

  • id: ID! - Contact ID to update
  • name: String - New contact name
  • email: String - New email address
  • phone: String - New phone number
  • taxId: String - New tax ID

Return Type: ContactNode

Authentication: Required

Credits: 1 (worker-queued)

deleteContact

Delete a contact.

mutation DeleteContact($id: ID!) {
  deleteContact(id: $id)
}

Parameters:

  • id: ID! - Contact ID to delete

Return Type: Boolean

Authentication: Required

Credits: 1 (worker-queued)

Document Mutations

createInvoice

Create a new sales invoice.

mutation CreateInvoice(
  $contactId: ID!
  $invoiceDate: DateTime!
  $dueDate: DateTime!
  $items: [InvoiceItemInput!]!
  $notes: String
) {
  createInvoice(
    contactId: $contactId
    invoiceDate: $invoiceDate
    dueDate: $dueDate
    items: $items
    notes: $notes
  ) {
    id
    invoiceNumber
    contactId
    invoiceDate
    dueDate
    status
    subtotal
    taxAmount
    total
    items {
      description
      quantity
      unitPrice
      lineTotal
    }
    createdAt
  }
}

Parameters:

  • contactId: ID! - Customer contact ID
  • invoiceDate: DateTime! - Invoice creation date
  • dueDate: DateTime! - Payment due date
  • items: [InvoiceItemInput!]! - Invoice line items
  • notes: String - Optional invoice notes

Return Type: InvoiceNode

Authentication: Required

Credits: 5 (worker-queued)

Example:

mutation CreateSalesInvoice {
  createInvoice(
    contactId: "con_1234567890abcdef"
    invoiceDate: "2024-01-15T00:00:00Z"
    dueDate: "2024-02-15T00:00:00Z"
    items: [
      {
        description: "Web Development Services"
        quantity: 40.0
        unitPrice: 125.00
      }
      {
        description: "Hosting Setup"
        quantity: 1.0
        unitPrice: 500.00
      }
    ]
    notes: "Thank you for your business!"
  ) {
    id
    invoiceNumber
    total
    status
  }
}

InvoiceItemInput Structure:

input InvoiceItemInput {
  description: String!  # Item description
  quantity: Decimal!    # Quantity ordered
  unitPrice: Decimal!   # Price per unit
  itemId: ID            # Optional catalog item reference
}

createBill

Create a new purchase bill from a vendor.

mutation CreateBill(
  $contactId: ID!
  $billDate: DateTime!
  $dueDate: DateTime!
  $items: [BillItemInput!]!
  $notes: String
) {
  createBill(
    contactId: $contactId
    billDate: $billDate
    dueDate: $dueDate
    items: $items
    notes: $notes
  ) {
    id
    billNumber
    contactId
    billDate
    dueDate
    status
    subtotal
    taxAmount
    total
    items {
      description
      quantity
      unitPrice
      lineTotal
    }
    createdAt
  }
}

Parameters:

  • contactId: ID! - Vendor contact ID
  • billDate: DateTime! - Bill date
  • dueDate: DateTime! - Payment due date
  • items: [BillItemInput!]! - Bill line items
  • notes: String - Optional bill notes

Return Type: BillNode

Authentication: Required

Credits: 5 (worker-queued)

BillItemInput Structure:

input BillItemInput {
  description: String!  # Item description
  quantity: Decimal!    # Quantity received
  unitPrice: Decimal!   # Price per unit
  itemId: ID            # Optional catalog item reference
}

recordInvoicePayment

Record a payment against an existing invoice.

mutation RecordInvoicePayment(
  $invoiceId: ID!
  $amount: Decimal!
  $paymentDate: DateTime!
  $paymentMethod: String!
  $reference: String
  $notes: String
) {
  recordInvoicePayment(
    invoiceId: $invoiceId
    amount: $amount
    paymentDate: $paymentDate
    paymentMethod: $paymentMethod
    reference: $reference
    notes: $notes
  ) {
    id
    invoiceId
    amount
    paymentDate
    paymentMethod
    reference
    notes
    createdAt
  }
}

Parameters:

  • invoiceId: ID! - Invoice ID to record payment against
  • amount: Decimal! - Payment amount
  • paymentDate: DateTime! - When payment was received
  • paymentMethod: String! - Payment method (check, wire, card, etc.)
  • reference: String - Reference number (check #, transaction ID)
  • notes: String - Optional payment notes

Return Type: PaymentNode

Authentication: Required

Credits: 2

Example:

mutation RecordPayment {
  recordInvoicePayment(
    invoiceId: "inv_1234567890abcdef"
    amount: 1250.00
    paymentDate: "2024-01-20T00:00:00Z"
    paymentMethod: "Check"
    reference: "Check #1234"
    notes: "Payment received via mail"
  ) {
    id
    amount
    paymentMethod
    reference
  }
}

recordBillPayment

Record a payment against an existing bill.

mutation RecordBillPayment(
  $billId: ID!
  $amount: Decimal!
  $paymentDate: DateTime!
  $paymentMethod: String!
  $reference: String
  $notes: String
) {
  recordBillPayment(
    billId: $billId
    amount: $amount
    paymentDate: $paymentDate
    paymentMethod: $paymentMethod
    reference: $reference
    notes: $notes
  ) {
    id
    billId
    amount
    paymentDate
    paymentMethod
    reference
    notes
    createdAt
  }
}

Parameters:

  • billId: ID! - Bill ID to record payment against
  • amount: Decimal! - Payment amount
  • paymentDate: DateTime! - When payment was made
  • paymentMethod: String! - Payment method
  • reference: String - Reference number
  • notes: String - Optional payment notes

Return Type: PaymentNode

Authentication: Required

Credits: 2 (worker-queued)

Billing Mutations

purchaseCredits

Purchase additional credits for your organization.

mutation PurchaseCredits(
  $creditAmount: Int!
  $successUrl: String
  $cancelUrl: String
) {
  purchaseCredits(
    creditAmount: $creditAmount
    successUrl: $successUrl
    cancelUrl: $cancelUrl
  ) {
    checkoutUrl
    creditsToAdd
    priceCents
    expiresAt
  }
}

Parameters:

  • creditAmount: Int! - Number of credits to purchase
  • successUrl: String - Redirect URL after successful payment
  • cancelUrl: String - Redirect URL if payment is cancelled

Return Type: CreditPurchaseNode

Authentication: Required

Credits: 0 (billing operation)

Example:

mutation BuyMoreCredits {
  purchaseCredits(
    creditAmount: 5000
    successUrl: "https://myapp.com/billing/success"
    cancelUrl: "https://myapp.com/billing/cancel"
  ) {
    checkoutUrl
    creditsToAdd
    priceCents
  }
}

Response:

{
  "data": {
    "purchaseCredits": {
      "checkoutUrl": "https://checkout.stripe.com/...",
      "creditsToAdd": 5000,
      "priceCents": 50000,
      "expiresAt": "2024-01-15T11:00:00Z"
    }
  }
}

Complex Mutation Examples

Complete Business Workflow

Create a customer, invoice, and record payment in sequence:

mutation CompleteSalesWorkflow {
  # 1. Create customer contact
  createContact: createContact(
    name: "TechStart Inc."
    contactType: CUSTOMER
    email: "billing@techstart.com"
    taxId: "US987654321"
  ) {
    id
    name
  }

  # Note: In GraphQL, you can't use the result of one mutation
  # as input to another in the same operation.
  # You'd need separate operations or use the returned ID.
}

# Separate operation using the contact ID
mutation CreateInvoiceAndPayment($contactId: ID!) {
  # 2. Create invoice for the customer
  createInvoice: createInvoice(
    contactId: $contactId
    invoiceDate: "2024-01-15T00:00:00Z"
    dueDate: "2024-02-15T00:00:00Z"
    items: [
      {
        description: "Software Development"
        quantity: 80.0
        unitPrice: 150.00
      }
    ]
  ) {
    id
    invoiceNumber
    total
  }
}

# Separate operation using the invoice ID
mutation RecordCustomerPayment($invoiceId: ID!) {
  # 3. Record payment
  recordPayment: recordInvoicePayment(
    invoiceId: $invoiceId
    amount: 12000.00
    paymentDate: "2024-01-20T00:00:00Z"
    paymentMethod: "Wire Transfer"
    reference: "TXN-2024-001"
  ) {
    id
    amount
    paymentMethod
  }
}

Bulk Account Creation

Create multiple accounts in a single operation (using aliases):

mutation CreateChartOfAccounts {
  cashAccount: createAccount(
    code: "1001"
    name: "Operating Cash"
    accountType: ASSET
    description: "Primary checking account"
  ) {
    id
    code
    name
  }

  arAccount: createAccount(
    code: "1101"
    name: "Accounts Receivable"
    accountType: ASSET
    description: "Money owed by customers"
  ) {
    id
    code
    name
  }

  salesAccount: createAccount(
    code: "4001"
    name: "Sales Revenue"
    accountType: REVENUE
    description: "Revenue from product sales"
  ) {
    id
    code
    name
  }

  expenseAccount: createAccount(
    code: "5101"
    name: "Office Rent"
    accountType: EXPENSE
    description: "Monthly office rent expense"
  ) {
    id
    code
    name
  }
}

Complex Transaction with Multiple Entries

Record a payroll transaction with multiple deductions:

mutation RecordPayrollTransaction {
  createTransaction(
    description: "Monthly payroll - January 2024"
    date: "2024-01-31T00:00:00Z"
    entries: [
      # Gross salary expense
      {
        accountId: "act_5101"
        entryType: DEBIT
        amount: 5000.00
        description: "Gross salary expense"
      }
      # Employee taxes withheld
      {
        accountId: "act_2201"
        entryType: DEBIT
        amount: 750.00
        description: "Employee tax withholdings"
      }
      # Employer taxes
      {
        accountId: "act_2202"
        entryType: DEBIT
        amount: 612.50
        description: "Employer payroll taxes"
      }
      # Cash payment (net pay)
      {
        accountId: "act_1001"
        entryType: CREDIT
        amount: 3750.00
        description: "Net salary payment"
        reference: "Check #1001"
      }
      # Taxes payable (to be remitted)
      {
        accountId: "act_2210"
        entryType: CREDIT
        amount: 1362.50
        description: "Payroll taxes payable"
      }
    ]
  ) {
    id
    description
    amount
    status
    entries {
      accountId
      entryType
      amount
      description
    }
  }
}

Error Handling

Business Logic Errors

{
  "errors": [
    {
      "message": "Transaction does not balance: debits (150.00) != credits (100.00)",
      "extensions": {
        "code": "BUSINESS_RULE_VIOLATION"
      }
    }
  ]
}

Validation Errors

{
  "errors": [
    {
      "message": "Account code '1001' already exists",
      "extensions": {
        "code": "VALIDATION_ERROR"
      }
    }
  ]
}

Authentication Errors

{
  "errors": [
    {
      "message": "API key is disabled",
      "extensions": {
        "code": "FORBIDDEN"
      }
    }
  ]
}

Insufficient Credits

{
  "errors": [
    {
      "message": "Insufficient credits remaining (required: 2, available: 1)",
      "extensions": {
        "code": "CREDIT_LIMIT_EXCEEDED"
      }
    }
  ]
}

Best Practices

Input Validation

Always validate inputs before mutations:

// Client-side validation
function validateTransaction(entries) {
  const totalDebits = entries
    .filter(e => e.entryType === 'DEBIT')
    .reduce((sum, e) => sum + parseFloat(e.amount), 0);

  const totalCredits = entries
    .filter(e => e.entryType === 'CREDIT')
    .reduce((sum, e) => sum + parseFloat(e.amount), 0);

  if (totalDebits !== totalCredits) {
    throw new Error(`Transaction does not balance: ${totalDebits}${totalCredits}`);
  }
}

Error Recovery

Handle errors gracefully:

async function safeMutation(mutation, variables) {
  try {
    const result = await client.mutate({
      mutation,
      variables
    });
    return result.data;
  } catch (error) {
    if (error.graphQLErrors) {
      // Handle GraphQL errors
      const businessError = error.graphQLErrors.find(
        e => e.extensions?.code === 'BUSINESS_RULE_VIOLATION'
      );
      if (businessError) {
        // Show user-friendly message
        alert('Transaction must balance (debits = credits)');
        return;
      }
    }

    if (error.networkError) {
      // Handle network errors
      console.error('Network error:', error.networkError);
    }

    throw error;
  }
}

Batch Operations

Group related mutations when possible:

// Instead of multiple separate calls
await createAccount(account1);
await createAccount(account2);
await createAccount(account3);

// Use aliases in single mutation
const result = await client.mutate({
  mutation: gql`
    mutation CreateMultipleAccounts {
      account1: createAccount(code: "1001", name: "Cash", accountType: ASSET) { id }
      account2: createAccount(code: "1101", name: "AR", accountType: ASSET) { id }
      account3: createAccount(code: "4001", name: "Sales", accountType: REVENUE) { id }
    }
  `
});

Credit Management

Monitor credit usage in mutations:

// Check credits before expensive operations
async function checkCredits(minimumRequired) {
  const { data } = await client.query({
    query: gql`query { organization { creditsRemaining } }`
  });

  if (data.organization.creditsRemaining < minimumRequired) {
    throw new Error(`Insufficient credits. Required: ${minimumRequired}, Available: ${data.organization.creditsRemaining}`);
  }
}

// Use before complex operations
await checkCredits(5); // For invoice creation
const invoice = await createInvoice(invoiceData);

Performance Considerations

Mutation Costs

OperationCreditsNotes
createAccount1Simple entity creation
updateAccount1Metadata updates only
createTransaction2Journal entry validation
createContact1Worker-queued
createInvoice5Complex document + PDF generation
recordInvoicePayment2Transaction creation
purchaseCredits0Billing operation

Optimizing Mutations

  1. Batch Related Operations:
mutation BulkAccountCreation {
  assets: createAccount(code: "1000", name: "Assets", accountType: ASSET) { id }
  liabilities: createAccount(code: "2000", name: "Liabilities", accountType: LIABILITY) { id }
  equity: createAccount(code: "3000", name: "Equity", accountType: EQUITY) { id }
}
  1. Minimize Return Data:
# ✅ Efficient - only return what you need
mutation CreateAccount {
  createAccount(code: "1001", name: "Cash", accountType: ASSET) {
    id  # Just the ID
  }
}

# ❌ Inefficient - returns everything
mutation CreateAccountVerbose {
  createAccount(code: "1001", name: "Cash", accountType: ASSET) {
    id
    code
    name
    accountType
    status
    createdAt
    updatedAt
    # ... all fields
  }
}
  1. Use Appropriate Return Fields:
mutation CreateInvoice {
  createInvoice(...) {
    id
    invoiceNumber
    total
    status
    # Don't return large nested objects unless needed
  }
}

GraphQL mutations in Crane Ledger provide powerful, type-safe operations for managing your accounting data. Each mutation includes comprehensive validation, error handling, and credit cost transparency to ensure reliable financial operations.


Need help?

Create a free account to access our support portal. Once signed in, use the Support tab in your dashboard to submit a support ticket — our team typically responds within 24 hours.