API Reference

The Kanalyze REST API lets you integrate financial data, variance reports, and AI narratives into your own systems. All endpoints are versioned at /api/v1.

Companies

List all companies in the authenticated organization.

Auth: Bearer token (session cookie or API key)

Response

Array of company objects.
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "name": "Acme Corp",
      "currency": "USD"
    }
  ],
  "error": null,
  "meta": null
}

Try it

curl -X GET "https://kanalyze.io/api/v1/companies" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

Create a new company within the organization.

Auth: Bearer token (session cookie or API key)

Request Body

Company name, optional industry and currency.
{
  "name": "Acme Corp",
  "industry": "SaaS",
  "currency": "USD"
}

Response

The created company object.
{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "Acme Corp",
    "currency": "USD",
    "createdAt": "2025-01-01T00:00:00Z"
  },
  "error": null,
  "meta": null
}

Try it

curl -X POST "https://kanalyze.io/api/v1/companies" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Acme Corp","currency":"USD"}'

Get a single company by ID.

Auth: Bearer token (session cookie or API key)

Response

The company object.
{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "Acme Corp"
  },
  "error": null,
  "meta": null
}

Try it

curl -X GET "https://kanalyze.io/api/v1/companies/{companyId}" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

Update a company name, industry, or currency.

Auth: Bearer token (session cookie or API key)

Request Body

Fields to update (all optional).
{
  "name": "Acme Corp Inc.",
  "currency": "CAD"
}

Response

The updated company object.
{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "Acme Corp Inc."
  },
  "error": null,
  "meta": null
}

Try it

curl -X PATCH "https://kanalyze.io/api/v1/companies/{companyId}" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Acme Corp Inc."}'

Soft-delete a company (sets deleted_at, data is retained).

Auth: Bearer token (session cookie or API key)

Response

204 No Content on success.
{}

Try it

curl -X DELETE "https://kanalyze.io/api/v1/companies/{companyId}" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

Uploads

Get a pre-signed S3 PUT URL to upload a financial file (CSV or XLSX). The client uploads directly to S3 using this URL, then calls /complete.

Auth: Bearer token (session cookie or API key)

Request Body

Company ID, file name, period range, upload type.
{
  "companyId": "uuid",
  "fileName": "jan-2025.csv",
  "uploadType": "ACTUALS",
  "periodStart": "2025-01-01",
  "periodEnd": "2025-01-31"
}

Response

Pre-signed URL and upload ID.
{
  "success": true,
  "data": {
    "uploadId": "uuid",
    "presignedUrl": "https://s3.amazonaws.com/...",
    "expiresAt": "2025-01-01T00:15:00Z"
  },
  "error": null,
  "meta": null
}

Try it

curl -X POST "https://kanalyze.io/api/v1/uploads/presign" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"companyId":"uuid","fileName":"jan-2025.csv","uploadType":"ACTUALS","periodStart":"2025-01-01","periodEnd":"2025-01-31"}'

Signal that the S3 upload is complete. Triggers the PARSE_FINANCIAL_UPLOAD background job.

Auth: Bearer token (session cookie or API key)

Request Body

Optional S3 ETag for deduplication.
{
  "s3ETag": "\"abc123\""
}

Response

Background job ID to poll for status.
{
  "success": true,
  "data": {
    "jobId": "uuid",
    "status": "PENDING"
  },
  "error": null,
  "meta": null
}

Try it

curl -X POST "https://kanalyze.io/api/v1/uploads/{uploadId}/complete" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"s3ETag":"\"abc123\""}'

Variance (BvA)

Live Budget vs Actual variance for a company, period, and budget. Returns line-level variance without persisting a snapshot.

Auth: Bearer token (session cookie or API key)

Response

Array of BvA lines with variance amounts and percentages. variancePercentage is a DECIMAL FRACTION of budget ("0.10" = 10%) — multiply by 100 for display.
{
  "success": true,
  "data": [
    {
      "accountName": "Revenue",
      "budgetAmount": "50000",
      "actualAmount": "55000",
      "varianceAmount": "5000",
      "variancePercentage": "0.10",
      "isFavorable": true
    }
  ],
  "error": null,
  "meta": null
}

Try it

curl -X GET "https://kanalyze.io/api/v1/companies/{companyId}/variance?budgetId=uuid&periodDate=2025-01-31" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

List persisted variance report snapshots for a company.

Auth: Bearer token (session cookie or API key)

Response

Paginated list of variance reports.
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "status": "COMPLETE",
      "periodDate": "2025-01-31",
      "lineCount": 42
    }
  ],
  "error": null,
  "meta": {
    "returned": 1
  }
}

Try it

curl -X GET "https://kanalyze.io/api/v1/companies/{companyId}/variance-reports?limit=20&offset=0" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

Create a persisted variance report snapshot. Use async:true to run as a background job (recommended for large datasets).

Auth: Bearer token (session cookie or API key)

Request Body

Budget ID, period date, variance thresholds, async flag.
{
  "budgetId": "uuid",
  "periodDate": "2025-01-31",
  "varianceThresholdAmount": "1000",
  "varianceThresholdPercent": "5",
  "async": true
}

Response

201 sync with report data, or 202 async with jobId.
{
  "success": true,
  "data": {
    "jobId": "uuid"
  },
  "error": null,
  "meta": null
}

Try it

curl -X POST "https://kanalyze.io/api/v1/companies/{companyId}/variance-reports" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"budgetId":"uuid","periodDate":"2025-01-31","async":true}'

Narratives

List AI-generated executive narratives for a company. The narrative EXPLAINS the deterministic health scorecard; it does not assign a grade. healthScore is historical only and is null on narratives generated from prompt narrative-executive-v2 onward — read the current grade from the health-trend endpoint.

Auth: Bearer token (session cookie or API key)

Response

Array of narrative objects. healthScore is null except on pre-v2 historical rows.
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "periodDate": "2025-01-31",
      "healthScore": null,
      "status": "COMPLETE"
    }
  ],
  "error": null,
  "meta": null
}

Try it

curl -X GET "https://kanalyze.io/api/v1/companies/{companyId}/narratives?periodDate=2025-01-31" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

Generate a new AI executive narrative for a period. Optionally tie it to a variance report for richer analysis.

Auth: Bearer token (session cookie or API key)

Request Body

Period date and optional variance report ID.
{
  "periodDate": "2025-01-31",
  "varianceReportId": "uuid"
}

Response

The generated narrative. healthScore is always null on new rows — the health grade comes from the deterministic scorecard, which the narrative explains.
{
  "success": true,
  "data": {
    "id": "uuid",
    "healthScore": null,
    "content": "Health score 68.4 (C). Revenue came in at $55K...",
    "status": "COMPLETE"
  },
  "error": null,
  "meta": null
}

Try it

curl -X POST "https://kanalyze.io/api/v1/companies/{companyId}/narratives" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"periodDate":"2025-01-31"}'

Budgets

List all budgets for a company.

Auth: Bearer token (session cookie or API key)

Response

Array of budget objects.
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "name": "FY2025 Budget",
      "fiscalYear": 2025,
      "budgetType": "APPROVED"
    }
  ],
  "error": null,
  "meta": null
}

Try it

curl -X GET "https://kanalyze.io/api/v1/companies/{companyId}/budgets" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

Create a new budget for a company.

Auth: Bearer token (session cookie or API key)

Request Body

Budget name, fiscal year, type.
{
  "name": "FY2025 Budget",
  "fiscalYear": 2025,
  "budgetType": "DRAFT"
}

Response

The created budget object.
{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "FY2025 Budget"
  },
  "error": null,
  "meta": null
}

Try it

curl -X POST "https://kanalyze.io/api/v1/companies/{companyId}/budgets" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"FY2025 Budget","fiscalYear":2025,"budgetType":"DRAFT"}'

Integrations

List active accounting integrations (QuickBooks Online, Xero) for a company.

Auth: Bearer token (session cookie or API key)

Response

Array of integration objects.
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "provider": "quickbooks",
      "status": "CONNECTED",
      "lastSyncedAt": "2025-01-31T08:00:00Z"
    }
  ],
  "error": null,
  "meta": null
}

Try it

curl -X GET "https://kanalyze.io/api/v1/companies/{companyId}/integrations" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

Initiate OAuth authorization for a new integration. Returns an authorization URL to redirect the user to.

Auth: Bearer token (session cookie or API key)

Request Body

Provider name.
{
  "provider": "quickbooks"
}

Response

OAuth authorization URL.
{
  "success": true,
  "data": {
    "authorizationUrl": "https://appcenter.intuit.com/connect/oauth2?..."
  },
  "error": null,
  "meta": null
}

Try it

curl -X POST "https://kanalyze.io/api/v1/companies/{companyId}/integrations" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"provider":"quickbooks"}'

Company Metrics

Push monthly operational metric values (headcount, billable hours, units, …) keyed by the company’s stable metric_key. The programmatic third entry path beside manual grid entry and CSV/XLSX import — pushed values power the operational and industry KPI sections. Create an API key with scope "metrics:write" under Settings → Developer → API keys. The batch is atomic: any unknown metric_key or invalid entry rejects the whole request and nothing is written. Omit blank months (blank never becomes 0; a pushed 0 is a real zero); the API cannot delete values — clearing a cell is a dashboard action.

Auth: API key with scope metrics:write

Request Body

Company id plus up to 5,000 entries. periodDate is the first of the month (YYYY-MM-01); value is a plain decimal string with up to 4 decimal places.
{
  "companyId": "uuid",
  "entries": [
    {
      "metricKey": "headcount",
      "periodDate": "2026-07-01",
      "value": "13"
    },
    {
      "metricKey": "billable_hours",
      "periodDate": "2026-07-01",
      "value": "1370.5"
    }
  ]
}

Response

Count of values written (created or updated).
{
  "success": true,
  "data": {
    "upserted": 2
  },
  "error": null,
  "meta": null
}

Try it

curl -X POST "https://kanalyze.io/api/v1/metrics/push" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"companyId":"uuid","entries":[{"metricKey":"headcount","periodDate":"2026-07-01","value":"13"}]}'

Auth / Me

Export all personal data for the authenticated user as a ZIP archive (GDPR right of portability).

Auth: Bearer token (session cookie or API key)

Response

ZIP file download containing JSON exports of all user data.

Try it

curl -X GET "https://kanalyze.io/api/v1/me/data-export" \
  -H "Authorization: Bearer knz_YOUR_API_KEY" \
  --output data-export.zip

Request account deletion (GDPR right to erasure). Soft-deletes the user and schedules data purge.

Auth: Bearer token (session cookie or API key)

Response

200 with confirmation message.
{
  "success": true,
  "data": {
    "message": "Account scheduled for deletion."
  },
  "error": null,
  "meta": null
}

Try it

curl -X DELETE "https://kanalyze.io/api/v1/me/account" \
  -H "Authorization: Bearer knz_YOUR_API_KEY"

All responses follow the envelope format: {success, data, error, meta}. Errors include an HTTP status code (400–503) and a machine-readable code field.

Rate limits: 300 req/min standard, 30 req/min for AI endpoints (narratives, chat).