Solarium
API Reference

Introduction

The Solarium API is a GraphQL API that gives you full programmatic access to your accounting data: accounts, contacts, journal entries, receipts, and financial reports.

All requests go to a single endpoint. Authentication is via API key (Bearer token) or JWT. Every query and mutation requires an organizationId. Your API key is scoped to one organization, so use the myOrganizations query to resolve it.

Base URL: https://solarium-api.lovemaple.app/graphql

Quickstart

Grab your API key from Settings > API Keys in the dashboard, then make your first request:

Request
# curl: resolve your org ID
curl -X POST https://solarium-api.lovemaple.app/graphql \
  -H "Authorization: Bearer sol_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ myOrganizations { id name } }"}'

# Python: full example
import httpx

API_URL = "https://solarium-api.lovemaple.app/graphql"
API_KEY = "sol_your_key_here"

def query(q, variables=None):
    resp = httpx.post(
        API_URL,
        json={"query": q, "variables": variables or {}},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    return resp.json()["data"]

# 1. Get your org ID
orgs = query("{ myOrganizations { id name } }")
org_id = orgs["myOrganizations"][0]["id"]

# 2. List accounts
accounts = query("""
  query($orgId: UUID!) {
    listAccounts(organizationId: $orgId, limit: 10) {
      items { id name accountType currentBalance }
      totalCount
    }
  }
""", {"orgId": org_id})

# 3. Record an expense
result = query("""
  mutation($orgId: UUID!) {
    createReceipt(
      organizationId: $orgId
      vendorName: "Subway"
      amount: "42.50"
      receiptDate: "2025-01-15"
      status: "processed"
    ) { receipt { id } success error }
  }
""", {"orgId": org_id})
Response
{
  "data": {
    "myOrganizations": [
      {
        "id": "a1b2c3d4-...",
        "name": "My Company Inc."
      }
    ]
  }
}

Authentication

Authenticate by passing your API key as a Bearer token in the Authorization header.

API keys are created in the Solarium dashboard at Settings > API Keys. API keys start with sol_ and are shown once at creation. Store them securely. Only the SHA-256 hash is stored on our end.

API keys are scoped to a single organization. You cannot access another organization's data with an API key. Admin operations (managing members, creating other API keys) are not available via API key.

Rate Limiting

API key requests are limited to 5,000 requests per hour per API key. The limit resets on a rolling 1-hour window from the first request.

Every API key response includes rate limit headers:

  • X-RateLimit-Limit: your hourly quota (5000)
  • X-RateLimit-Remaining: requests left in the current window

If you exceed the limit, requests return 429 Too Many Requests with a Retry-After header. JWT-authenticated requests (dashboard sessions) are not rate limited.

Request
# API Key auth (recommended for integrations)
curl -X POST https://solarium-api.lovemaple.app/graphql \
  -H "Authorization: Bearer sol_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"query": "{ myOrganizations { id name } }"}'

# JWT auth (for user sessions)
curl -X POST https://solarium-api.lovemaple.app/graphql \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"query": "{ myOrganizations { id name } }"}'
Response
# Unauthenticated request
{
  "errors": [
    { "message": "Authentication Required." }
  ]
}

# Wrong Organization
{
  "errors": [
    { "message": "API Key Not Authorized For This Organization." }
  ]
}

# Rate limit exceeded (429)
{
  "errors": [
    { "message": "Rate Limit Exceeded. 5,000 Requests/Hour." }
  ]
}
# Headers: Retry-After: 60

Errors

The Solarium API returns errors in a consistent JSON shape. GraphQL requests always return 200 OK at the HTTP level. Errors appear in the errors array of the response body.

The only exceptions are 429 Too Many Requests (rate limit) and 400 Bad Request (malformed JSON or invalid GraphQL syntax), which return non-200 HTTP status codes.

Error Object

Each error has a message string. Some errors include a path array indicating which field triggered the error.

Common Errors

MessageCause
Authentication Required.No token or API key provided
API Key Not Authorized For This Organization.API key belongs to a different organization
Organization Not Found.Invalid organizationId
Not A Member Of This Organization.JWT user is not a member of this organization
API Keys Cannot Perform Admin Operations.API key used for an admin-only mutation (e.g. managing members)
Must Be Owner Or Admin.Insufficient role for this operation
Rate Limit Exceeded. 5,000 Requests/Hour.API key exceeded hourly quota (HTTP 429)

Mutation Errors

Mutations return a success boolean and an error string instead of throwing. A failed mutation returns success: false with a human-readable error. The HTTP status is still 200.

Request
# Successful mutation
mutation {
  createAccount(
    organizationId: "a1b2c3d4-..."
    name: "Office Supplies"
    accountType: "expense"
    classification: "expense"
  ) {
    account { id name }
    success
    error
  }
}
Response
# GraphQL-level error (in errors array)
{
  "errors": [
    {
      "message": "Authentication Required.",
      "path": ["createAccount"]
    }
  ],
  "data": { "createAccount": null }
}

# Mutation-level error (success: false)
{
  "data": {
    "createAccount": {
      "account": null,
      "success": false,
      "error": "Account with this name already exists."
    }
  }
}

# Rate limit error (HTTP 429)
{
  "errors": [
    { "message": "Rate Limit Exceeded. 5,000 Requests/Hour." }
  ]
}

Accounts

POST

List Accounts

Returns a paginated list of accounts in the chart of accounts. Supports search by name and sorting.

Parameters

organizationIdUUID!required

Organization ID

limitInt

Page size (default 25, max 200)

offsetInt

Number of records to skip

searchString

Filter by name (case-insensitive)

sortByString

Field to sort by (e.g. name, currentBalance)

sortDirString

asc or desc (default asc)

POST

Create an Account

Creates a new account in the chart of accounts.

Parameters

organizationIdUUID!required

Organization ID

nameString!required

Account name

accountTypeString!required

bank, accounts_receivable, other_current_asset, fixed_asset, credit_card, accounts_payable, other_current_liability, long_term_liability, equity, income, cogs, expense, other_income, other_expense

classificationString!required

asset, liability, equity, revenue, expense

subTypeString

Optional sub-type label

parentIdUUID

Parent account ID for hierarchical accounts

List Accounts — Request
query {
  listAccounts(
    organizationId: "a1b2c3d4-..."
    limit: 10
    search: "expense"
  ) {
    items {
      id
      name
      accountType
      classification
      subType
      currentBalance
      active
    }
    totalCount
    pageSize
    offset
  }
}
Response
{
  "data": {
    "listAccounts": {
      "items": [
        {
          "id": "f7e6d5c4-...",
          "name": "Office Supplies",
          "accountType": "expense",
          "classification": "expense",
          "subType": "",
          "currentBalance": 1250.00,
          "active": true
        }
      ],
      "totalCount": 24,
      "pageSize": 10,
      "offset": 0
    }
  }
}
Create an Account — Request
mutation {
  createAccount(
    organizationId: "a1b2c3d4-..."
    name: "Office Supplies"
    accountType: "expense"
    classification: "expense"
  ) {
    account {
      id
      name
      accountType
      classification
    }
    success
    error
  }
}
Response
{
  "data": {
    "createAccount": {
      "account": {
        "id": "f7e6d5c4-...",
        "name": "Office Supplies",
        "accountType": "expense",
        "classification": "expense"
      },
      "success": true,
      "error": null
    }
  }
}

Contacts

POST

List Contacts

Returns a paginated list of vendors and customers.

Parameters

organizationIdUUID!required

Organization ID

limitInt

Page size (default 25, max 200)

offsetInt

Number of records to skip

searchString

Filter by name

POST

Create a Contact

Creates a new vendor or customer.

Parameters

organizationIdUUID!required

Organization ID

contactTypeString!required

vendor or customer

displayNameString!required

Contact name

emailString

Email address

phoneString

Phone number

List Contacts — Request
query {
  listContacts(
    organizationId: "a1b2c3d4-..."
    limit: 10
    search: "uber"
  ) {
    items {
      id
      contactType
      displayName
      email
      phone
      balance
      active
    }
    totalCount
  }
}
Response
{
  "data": {
    "listContacts": {
      "items": [
        {
          "id": "b2c3d4e5-...",
          "contactType": "vendor",
          "displayName": "Uber",
          "email": "",
          "phone": "",
          "balance": 0,
          "active": true
        }
      ],
      "totalCount": 1
    }
  }
}
Create a Contact — Request
mutation {
  createContact(
    organizationId: "a1b2c3d4-..."
    contactType: "vendor"
    displayName: "Staples"
    email: "[email protected]"
  ) {
    contact { id displayName contactType }
    success
    error
  }
}
Response
{
  "data": {
    "createContact": {
      "contact": {
        "id": "c3d4e5f6-...",
        "displayName": "Staples",
        "contactType": "vendor"
      },
      "success": true,
      "error": null
    }
  }
}

Journal Entries

POST

List Journal Entries

Returns a paginated list of journal entries with their debit/credit lines.

Parameters

organizationIdUUID!required

Organization ID

limitInt

Page size (default 25, max 200)

offsetInt

Number of records to skip

searchString

Filter by memo

POST

Create a Journal Entry

Creates a manual journal entry with debit/credit lines. Total debits must equal total credits.

Parameters

organizationIdUUID!required

Organization ID

txnDateDate!required

Transaction date (YYYY-MM-DD)

totalAmountDecimal!required

Total amount (sum of debits)

memoString

Description / memo

lines[JournalEntryLineInput!]!required

Array of line items. Each: accountId (UUID!), debit (Decimal), credit (Decimal), description (String)

List Journal Entries — Request
query {
  listJournalEntries(
    organizationId: "a1b2c3d4-..."
    limit: 5
  ) {
    items {
      id
      txnDate
      totalAmount
      memo
      sourceType
      lines {
        id
        debit
        credit
        description
        account { id name }
      }
    }
    totalCount
  }
}
Response
{
  "data": {
    "listJournalEntries": {
      "items": [
        {
          "id": "d4e5f6a7-...",
          "txnDate": "2025-01-15",
          "totalAmount": "500.00",
          "memo": "Office rent — January",
          "sourceType": "manual",
          "lines": [
            {
              "id": "e5f6a7b8-...",
              "debit": "500.00",
              "credit": "0.00",
              "description": "Rent",
              "account": { "id": "...", "name": "Rent Expense" }
            },
            {
              "id": "f6a7b8c9-...",
              "debit": "0.00",
              "credit": "500.00",
              "description": "Paid",
              "account": { "id": "...", "name": "Checking" }
            }
          ]
        }
      ],
      "totalCount": 142
    }
  }
}
Create a Journal Entry — Request
mutation {
  createJournalEntry(
    organizationId: "a1b2c3d4-..."
    txnDate: "2025-01-15"
    totalAmount: "500.00"
    memo: "Office rent — January"
    lines: [
      {
        accountId: "rent-expense-uuid"
        debit: "500.00"
        credit: "0"
      },
      {
        accountId: "checking-uuid"
        debit: "0"
        credit: "500.00"
      }
    ]
  ) {
    journalEntry { id txnDate totalAmount memo }
    success
    error
  }
}
Response
{
  "data": {
    "createJournalEntry": {
      "journalEntry": {
        "id": "d4e5f6a7-...",
        "txnDate": "2025-01-15",
        "totalAmount": "500.00",
        "memo": "Office rent — January"
      },
      "success": true,
      "error": null
    }
  }
}

Receipts

POST

List Receipts

Returns a paginated list of receipts/expenses.

Parameters

organizationIdUUID!required

Organization ID

limitInt

Page size (default 25, max 200)

offsetInt

Number of records to skip

searchString

Filter by vendor name

POST

Create a Receipt

Creates a receipt and its linked journal entry in one call. This is the primary way to record an expense via the API.

Parameters

organizationIdUUID!required

Organization ID

vendorNameString

Vendor / merchant name

amountDecimal

Total amount including tax

taxAmountDecimal

Tax portion (HST/GST)

receiptDateDate

Date of purchase (YYYY-MM-DD)

statusString

draft, pending, processed, reviewed (default pending)

categoryIdUUID

Expense account to categorize under

paymentAccountIdUUID

Bank/credit card account used to pay

List Receipts — Request
query {
  listReceipts(
    organizationId: "a1b2c3d4-..."
    limit: 10
  ) {
    items {
      id
      vendorName
      amount
      taxAmount
      receiptDate
      status
      source
      category { id name }
      paymentAccount { id name }
    }
    totalCount
  }
}
Response
{
  "data": {
    "listReceipts": {
      "items": [
        {
          "id": "e5f6a7b8-...",
          "vendorName": "Subway",
          "amount": "42.50",
          "taxAmount": "5.53",
          "receiptDate": "2025-01-15",
          "status": "processed",
          "source": "api",
          "category": {
            "id": "...",
            "name": "Meals & Entertainment"
          },
          "paymentAccount": {
            "id": "...",
            "name": "Visa *4242"
          }
        }
      ],
      "totalCount": 89
    }
  }
}
Create a Receipt — Request
mutation {
  createReceipt(
    organizationId: "a1b2c3d4-..."
    vendorName: "Subway"
    amount: "42.50"
    taxAmount: "5.53"
    receiptDate: "2025-01-15"
    status: "processed"
    categoryId: "meals-expense-uuid"
    paymentAccountId: "visa-uuid"
  ) {
    receipt {
      id
      vendorName
      amount
      receiptDate
      status
      category { id name }
      paymentAccount { id name }
    }
    success
    error
  }
}
Response
{
  "data": {
    "createReceipt": {
      "receipt": {
        "id": "e5f6a7b8-...",
        "vendorName": "Subway",
        "amount": "42.50",
        "receiptDate": "2025-01-15",
        "status": "processed",
        "category": {
          "id": "...",
          "name": "Meals & Entertainment"
        },
        "paymentAccount": {
          "id": "...",
          "name": "Visa *4242"
        }
      },
      "success": true,
      "error": null
    }
  }
}

Reports

POST

Dashboard Summary

High-level financial snapshot: revenue, expenses, net income, receipt counts for a date range.

Parameters

organizationIdUUID!required

Organization ID

startDateDate

Start of range (defaults to start of current month)

endDateDate

End of range (defaults to today)

POST

Income Statement

Full profit & loss report broken down by account.

Parameters

organizationIdUUID!required

Organization ID

startDateDate

Start of range

endDateDate

End of range

POST

Account Balances

Current balance for every account in the chart of accounts.

Parameters

organizationIdUUID!required

Organization ID

Dashboard Summary — Request
query {
  dashboardSummary(
    organizationId: "a1b2c3d4-..."
    startDate: "2025-01-01"
    endDate: "2025-03-31"
  ) {
    totalRevenue
    totalExpenses
    netIncome
    receiptCount
    unreviewedCount
  }
}
Response
{
  "data": {
    "dashboardSummary": {
      "totalRevenue": "84500.00",
      "totalExpenses": "52300.00",
      "netIncome": "32200.00",
      "receiptCount": 89,
      "unreviewedCount": 3
    }
  }
}
Income Statement — Request
query {
  incomeStatement(
    organizationId: "a1b2c3d4-..."
    startDate: "2025-01-01"
    endDate: "2025-03-31"
  ) {
    incomeAccounts { accountId accountName total }
    expenseAccounts { accountId accountName total }
    totalIncome
    totalExpenses
    netIncome
  }
}
Response
{
  "data": {
    "incomeStatement": {
      "incomeAccounts": [
        { "accountId": "...", "accountName": "Sales", "total": "84500.00" }
      ],
      "expenseAccounts": [
        { "accountId": "...", "accountName": "Rent", "total": "18000.00" },
        { "accountId": "...", "accountName": "Payroll", "total": "28000.00" },
        { "accountId": "...", "accountName": "Office Supplies", "total": "6300.00" }
      ],
      "totalIncome": "84500.00",
      "totalExpenses": "52300.00",
      "netIncome": "32200.00"
    }
  }
}
Account Balances — Request
query {
  accountBalances(organizationId: "a1b2c3d4-...") {
    accountId
    accountName
    accountType
    classification
    balance
  }
}
Response
{
  "data": {
    "accountBalances": [
      {
        "accountId": "...",
        "accountName": "Checking",
        "accountType": "bank",
        "classification": "asset",
        "balance": "24680.50"
      },
      {
        "accountId": "...",
        "accountName": "Accounts Receivable",
        "accountType": "accounts_receivable",
        "classification": "asset",
        "balance": "12400.00"
      }
    ]
  }
}

Batch Operations

All batch mutations accept up to 500 items per call. Failures are per-item. One bad record does not roll back the others. Each item runs in its own database savepoint.

POST

Batch Create Accounts

Create up to 500 accounts in a single call.

Parameters

organizationIdUUID!required

Organization ID

items[AccountInput!]!required

Array of accounts. Each: name (String!), accountType (String!), classification (String!), subType (String), parentId (UUID)

POST

Batch Create Contacts

Create up to 500 contacts in a single call.

Parameters

organizationIdUUID!required

Organization ID

items[ContactInput!]!required

Array of contacts. Each: contactType (String!), displayName (String!), email (String), phone (String)

POST

Batch Create Journal Entries

Create up to 500 journal entries in a single call. Each entry includes its own lines.

Parameters

organizationIdUUID!required

Organization ID

items[JournalEntryInput!]!required

Array of entries. Each: txnDate (Date!), totalAmount (Decimal!), memo (String), lines ([JELineInput!]!)

POST

Batch Create Receipts

Record up to 500 expenses in a single call. Each creates a receipt + journal entry.

Parameters

organizationIdUUID!required

Organization ID

items[ReceiptInput!]!required

Array of receipts. Each: vendorName (String!), amount (Decimal!), receiptDate (Date!), categoryId (UUID), paymentAccountId (UUID), taxAmount (Decimal)

Batch Create Accounts — Request
mutation {
  batchCreateAccounts(
    organizationId: "a1b2c3d4-..."
    items: [
      { name: "Office Supplies", accountType: "expense", classification: "expense" },
      { name: "Travel", accountType: "expense", classification: "expense" },
      { name: "Software", accountType: "expense", classification: "expense" }
    ]
  ) {
    created
    errors { index error }
  }
}
Response
{
  "data": {
    "batchCreateAccounts": {
      "created": 3,
      "errors": []
    }
  }
}
Batch Create Contacts — Request
mutation {
  batchCreateContacts(
    organizationId: "a1b2c3d4-..."
    items: [
      { contactType: "vendor", displayName: "Staples" },
      { contactType: "vendor", displayName: "Amazon" }
    ]
  ) {
    created
    errors { index error }
  }
}
Response
{
  "data": {
    "batchCreateContacts": {
      "created": 2,
      "errors": []
    }
  }
}
Batch Create Journal Entries — Request
mutation {
  batchCreateJournalEntries(
    organizationId: "a1b2c3d4-..."
    items: [
      {
        txnDate: "2025-01-15"
        totalAmount: "100.00"
        memo: "Office supplies"
        lines: [
          { accountId: "expense-uuid", debit: "100.00", credit: "0" },
          { accountId: "bank-uuid", debit: "0", credit: "100.00" }
        ]
      }
    ]
  ) {
    created
    errors { index error }
  }
}
Response
{
  "data": {
    "batchCreateJournalEntries": {
      "created": 1,
      "errors": []
    }
  }
}
Batch Create Receipts — Request
mutation {
  batchCreateReceipts(
    organizationId: "a1b2c3d4-..."
    items: [
      { vendorName: "Subway", amount: "42.50", receiptDate: "2025-01-15" },
      { vendorName: "Tim Hortons", amount: "5.99", receiptDate: "2025-01-15" }
    ]
  ) {
    created
    errors { index error }
  }
}
Response
{
  "data": {
    "batchCreateReceipts": {
      "created": 2,
      "errors": []
    }
  }
}