DEVELOPER

Paidwell API

A small REST API for logging consulting time, reading entries, managing projects, and pulling hours/revenue rollups. Any tool that can make an authenticated HTTP request — an agent platform, a cron script, curl — can drive it.

Using Claude, ChatGPT, or Cursor? Use the MCP server

Paidwell ships a remote MCP server at /mcp (streamable HTTP, OAuth sign-in — no key handling). Add it as a custom connector and you get job-shaped tools — log_call, whats_unbilled, find_gaps — plus a drafts workflow where inferred work waits for your approval. Per-client setup lives in Settings → Connect your AI.

Scripts and custom agents: one-step key setup

In Settings → API access create a key (pick a scope: full, log-only, or read-only), then hit Copy agent setup prompt. That copies a full instruction set — with your key and base URL baked in — that any agent can use to build a Paidwell skill. No manual wiring.

AUTHENTICATION

Every request needs a Paidwell API key (mint one under Settings → API access; the API is free on every plan, Free tier included). Send it as either header. Keys are secret — they can create, edit, and delete your billing data. Only a SHA-256 hash is stored; the raw paidwell_sk_… secret is shown once, at creation.

Authorization: Bearer paidwell_sk_…
# or
X-API-Key: paidwell_sk_…

Base URL: /api/v1 on this host. Verify a key:

curl https://<your-paidwell-host>/api/v1/ping \
  -H "Authorization: Bearer paidwell_sk_…"
# → {"ok":true,"service":"paidwell","authenticatedAs":"<label>", …}
ENDPOINTS

All paths under /api/v1. Bodies and responses are JSON. Every request is scoped to the key owner’s own data.

GET/ping
Health/auth check. Returns the authenticated key’s label.
GET/projects
List projects. Optional ?archived=true|false.
POST/projects
Create a project. Only name is required; call-billing fields are optional.
{ "name": "Acme", "client": "Acme Inc", "hourlyRate": 200,
  "callMinimumMinutes": 60, "callPaddingMinutes": 60, "roundToMinutes": 60 }
GET/entries
List entries. Query params: projectId, from, to (YYYY-MM-DD, inclusive), billable, billed, search, limit (≤1000, default 100), proposed (true lists the draft approve-queue; drafts are excluded by default).
POST/entries
Log time — the main surface. Identify the project by projectId or project (name, case-insensitive); an optional non-Standard rate by rateId or rate (its label). Express duration three ways, and always pass an explicit date.
# human duration string
{ "project": "Acme", "description": "Architecture review", "duration": "1h30m", "date": "2026-05-31" }

# explicit minutes
{ "project": "Acme", "description": "Bugfix", "durationMinutes": 45, "date": "2026-05-31" }

# a CALL — applies the project's billing rules (minimum → padding → rounding)
{ "project": "Acme", "description": "Kickoff call", "isCall": true, "bookedDuration": "1h" }

Two agent-trust flags: "proposed": true lands the entry in the user’s in-app Inbox as a draft (invisible to every report until approved — use for anything inferred), and "dryRun": true computes the billing math and returns the would-be entry without saving anything.

GET/entries/{id}
Read one entry.
PATCH/entries/{id}
Partial update. Any of description, date, durationMinutes, billable, billed, invoiceRef (send null to clear it), proposed (only false — approving a draft; an entry can never be demoted back to a draft).
DELETE/entries/{id}
Delete one entry.
GET/summary
Hours + revenue rollup, overall and per project. Optional from, to, projectId. Returns { totals: { hours, billableHours, unbilledHours, revenueEstimate, unbilledRevenueEstimate, entryCount }, byProject: [...] }.
GET/gaps
Weekdays with no (or under-threshold) logged time — for cross-referencing against a calendar. Required from, to (≤92 days); optional minHours. Weekends skipped; future days are never gaps; drafts don’t count as logged.
KEY SCOPES

Keys carry a least-privilege scope chosen at mint time: full (read, log, edit, billing — your own agents), log (read + create entries only — a calendar bot), or read(read-only — a bookkeeper’s agent). Out-of-scope requests return 403.

CALL BILLING

When isCall: true, the booked duration is transformed by the project’s rules in order: minimum → + padding → round to nearest N (floored at one unit). Example: a 1h booked call with min 60, padding 60, round 60 bills 2h. A short call never collapses below one rounding unit.

ERRORS

Errors return { "error": { "code", "message" } } with a matching HTTP status: 400 bad_request · 401 unauthorized · 403 forbidden (revoked key) · 404 not_found · 429 rate_limited (>120 req/min per key) · 500 server_error.

Get an API keyopenapi.json