Download client timesheets

How to list the client timesheet documents a token may see and download them as HTML, PDF, or CSV through the API or the MCP get_timesheet tool.

Written for
integration-developer
Roles
owner, admin, project-manager, billing-contact
Requires
API token with the read scope; Document access: Owner or Admin, a client-wide document grant, project manager, or billing contact with a full login
Feature
documents

Every committed billing run produces client timesheet documents: an HTML version, a PDF, and a CSV, either one combined document per client or one per project depending on the client's timesheet layout. This recipe fetches them for an accounting or archiving system.

Who may download

The token sees the documents its owner may download in the app:

Owner Documents
Owner or Admin All
Workspace user with a client-wide document grant That client's documents, plus project documents of projects they manage
Project manager without a grant Project-scoped documents of their projects
Billing contact (client user) with a full login Their own client's documents
Anyone else None

A project-scoped token is further limited to its named projects and never receives combined (client-wide) documents. The list's scope field (all, client, projects, none) tells you which rule applied.

1. List documents

curl "https://saq.no/api/w/acme/documents?clientId=01924c3e-0000-7000-8000-000000000007" \
  -H "Authorization: Bearer saq_YOUR_TOKEN"

Both query parameters are optional: clientId narrows to one client, runId to one billing run. Response:

{
  "scope": "all",
  "documents": [
    {
      "id": "01924c3e-0000-7000-8000-000000000040",
      "runId": "01924c3e-0000-7000-8000-000000000041",
      "number": "ACME-2026-09-001",
      "scope": "combined",
      "projectId": null,
      "projectName": null,
      "clientId": "01924c3e-0000-7000-8000-000000000007",
      "clientName": "Acme AS",
      "periodStart": "2026-08-01",
      "format": "pdf",
      "version": 1,
      "locale": "nb",
      "checksum": "3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "bytes": 48213,
      "voided": false,
      "redacted": false,
      "createdAt": "2026-09-02T07:30:00.000Z",
      "deliveries": [
        {
          "email": "invoices@acme.example",
          "state": "sent",
          "detail": null,
          "attemptedAt": "2026-09-02T07:31:00.000Z"
        }
      ]
    }
  ]
}

Each format is its own document with its own id; filter on format (html, pdf, csv) and skip documents with voided: true. checksum lets you verify a stored copy; deliveries shows the emails SAQ sent to the client's billing recipients.

2. Download

curl -L "https://saq.no/api/w/acme/documents/01924c3e-0000-7000-8000-000000000040/download" \
  -H "Authorization: Bearer saq_YOUR_TOKEN" \
  -o ACME-2026-09-001.pdf

The response streams the bytes with Content-Type application/pdf, text/csv; charset=utf-8, or text/html; charset=utf-8, and a Content-Disposition of attachment; filename="<number>.<format>" (inline for HTML). A document the owner may not download answers 404 not_found.

3. Through MCP

The MCP tool get_timesheet does both steps. Without arguments (or with clientId) it returns the same list as above. With documentId it returns the document's metadata and its content: encoding: "base64" with the PDF bytes, or encoding: "utf-8" with the HTML or CSV text.

{
  "name": "get_timesheet",
  "arguments": { "documentId": "01924c3e-0000-7000-8000-000000000040" }
}

Notes

  • Documents are immutable. A redaction or a voided run produces a new version or marks the document voided; re-list rather than caching ids forever.
  • The CSV holds the same lines as the PDF: date, category, ticket, billing description, hours, rate, amount, and outcome (charged, covered by a time bank, or free), followed by the bank ledger.
  • The PDF format exists only when the deployment produces PDFs; HTML and CSV are always produced.