Built for AI Agents

Agency Dispatch Pro API

Full CRUD access to process serving, investigations, documents, billing, and notary work — everything a human staff member can do, an API key can do too.

No articles match your search. Try a different keyword or .
Overview
What this API is and who it's for

The /api/v1/ REST API gives full CRUD access to everything a staff member can do in the app: process serving and investigation cases, case notes and updates, documents, clients, billing (invoices, payouts, credit, retainers), quotes, time and expense tracking, and — for agencies with the Notary module enabled — signing clients, jobs, journal, documents, and invoices.

It's designed to be driven by an AI agent ("AI employee") as easily as by a human developer: JSON in, JSON out, one consistent auth header shape across the whole product, and a machine-readable OpenAPI schema at /api/v1/swagger.json.

API access requires a Professional or Enterprise subscription tier. Notary endpoints additionally require the agency's Notary module to be enabled and the calling key to carry the Notary scope (see Scoped Keys below).

Authentication
One header format, one key type

Send the raw API key in the Authorization header on every request:

Authorization: ApiKey adp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

This is not a Bearer token — do not prefix the header value with the word "Bearer". The same agency key is used for every endpoint, including Notary; which endpoints it can actually reach is determined by its granted scopes (see Scoped Keys below), not by anything in the header itself.

Keys are generated at Agency → Settings → API Keys by a user with API access permission.

Tip: The raw key is shown exactly once, at generation time. It is never displayed again — only a short prefix is kept for display in the key list. Store it securely wherever your AI agent's credentials live.

Revoking a key takes effect immediately — any request already in flight may still complete, but every subsequent request with that key is rejected with 401 Unauthorized. Deleting a key (vs. revoking) permanently removes the record after revocation; revoke first if you might need the usage history later.

Scoped Keys
Give different AI agents different access levels

When generating a key, you can restrict it to one or more resource areas: ProcessServing, Investigations, Clients, Documents, Billing, Quotes, TimeAndExpense, Users, Notary. Leaving every box unchecked grants full access (matches the original, pre-scoping behavior) — this is a whitelist, not a blacklist.

A request to an endpoint outside a key's granted scopes returns 403 Forbidden with a body like:

{ "error": "This API key does not have the 'Billing' scope." }

Use this to give one AI agent read/write access to Process Serving only, another to Billing only, and so on — least-privilege per agent rather than one all-powerful key shared everywhere.

Note: Voiding a notary journal entry (POST /api/v1/notary/journal/{id}/void) requires the additional NotaryJournalVoid scope on top of Notary — a key can read/write the rest of the Notary surface without it.
Rate Limits
Per key, not per IP

The API allows 600 requests per minute per key (a fixed 1-minute window, not a rolling average). Exceeding it returns 429 Too Many Requests with no queueing — the request is rejected outright rather than delayed. Back off and retry after the window resets.

Response Conventions
What every response looks like

All request and response bodies are JSON. Enum values (status, type, etc.) serialize as their string name, never a raw integer — e.g. "status": "InProgress", not "status": 2.

List endpoints that support pagination return:

{ "total": 214, "page": 1, "pageSize": 50, "items": [ ... ] }

A successful create typically returns 201 Created with the new record's id; a successful update returns 200 OK; a successful delete returns 204 No Content.

Side Effects
Actions that reach outside the database

These endpoints have no extra AI-specific safety net — they do exactly what the equivalent button does in the human UI, so treat them with the same care you'd want a new employee to use:

  • POST /api/v1/billing/invoices/{id}/send — creates a real Stripe payment link (if the agency has Stripe enabled) and emails the client.
  • POST /api/v1/billing/invoices/{id}/mark-paid — charges linked time/expense entries, bumps a retainer balance, writes a top-up credit entry, deactivates the Stripe link, and syncs to Zoho Books.
  • POST /api/v1/billing/payouts/{id}/send — initiates a real Stripe Connect transfer to the contractor, if one is configured.
  • POST /api/v1/clients — best-effort syncs the new client to Zoho Books and sends a real client-portal invite email if an email address is given.
  • POST /api/v1/documents/email and POST /api/v1/notary/signing-clients/{id}/contacts/{contactId}/invite-portal — send real emails to real recipients.

If a payout's method is StripeConnect but the contractor has no Stripe Connect account configured, the payout silently flips to Sent with zero money actually transferred — there's no error and no different status. Check the contractor's Connect account is set up before relying on this to actually pay someone.

Immutable Records
What the API deliberately will not let you change

Notes on Process Serving and Investigation cases can be added and edited, but there is no delete endpoint — by design, matching the human UI, which has never supported deleting a note either. Don't build a workflow that assumes a note can be removed.

GET /api/v1/notary/journal and POST /api/v1/notary/journal/{id}/void are the only two operations on this table, and that will not change. Most US states legally require a notary's journal sequence numbers to never be renumbered, reshuffled, or deleted — a mistaken entry gets voided with a reason, not removed. A journal entry is created automatically, as a side effect, whenever POST /api/v1/notary/signing-jobs/{id}/notarial-acts succeeds; there is no way to create one directly.

Unlike the human UI — which lets a staff member set a signing job to any status from any other with no validation — the API enforces the documented lifecycle: Inquiry → Confirmed → InProgress → Completed → DocsShipped → Invoiced → Paid, with Cancelled reachable from any non-terminal state and nothing reachable from Cancelled. An out-of-order transition returns 400 Bad Request. This is intentionally stricter than the UI — an automated agent is more likely than a human clicking a dropdown to attempt an invalid jump, and the consequences (a job silently marked Paid with nothing actually invoiced) are worse.

Toggling a note or case update's client-visible flag via the API changes only that flag. In the human UI, doing the same thing also emails the client a notification — that email is tied to the identity of the signed-in staff member sending it, which doesn't exist under API-key auth, so it isn't sent. If your workflow depends on the client being notified, send that notification through your own integration.

PII Handling
What's encrypted, and what the API will show you

Sensitive identifiers — a signer's driver's license/passport/state ID number on a notary signing job — are encrypted at rest and never returned in plaintext by any API response, including GET requests you authenticated for yourself. Where the UI shows a masked value, the API returns the same masked value (e.g. "idNumberMasked": "****1234") instead of the ciphertext or the real number. There is currently no endpoint that returns a decrypted ID number under any circumstance.

No Idempotency Keys
Say this plainly so nobody assumes otherwise

None of these endpoints support an idempotency key today. If a create request (a new case, invoice, document, notarial act, etc.) times out or the response is lost, retrying it will create a second record rather than safely no-op. If your agent's HTTP client retries automatically on timeout, build your own de-duplication check (e.g. search for a matching record first) before retrying a POST.

Process Serving
/api/v1/ps · scope: ProcessServing
  • GET/POST /api/v1/ps, GET/PUT/DELETE /api/v1/ps/{id} — cases
  • GET/POST /api/v1/ps/{id}/attempts, PUT/DELETE .../attempts/{attemptId} — service attempts
  • POST/PUT/DELETE /api/v1/ps/{id}/updates/{updateId?} — case updates (full CRUD)
  • POST /api/v1/ps/{id}/notes, PUT .../notes/{noteId}, POST .../notes/{noteId}/toggle-visible — notes (no delete — see Immutable Records)
Investigations
/api/v1/inv · scope: Investigations
  • GET/POST /api/v1/inv, GET/PUT/DELETE /api/v1/inv/{id} — cases
  • POST/PUT/DELETE /api/v1/inv/{id}/updates/{updateId?}, POST .../notes, POST .../notes/{noteId}/toggle-visible (no note edit — the UI has none either)
  • POST/DELETE /api/v1/inv/{id}/subjects/{subjectId?} and nested .../addresses, .../phones, .../vehicles (add + delete)
  • GET/PUT /api/v1/inv/{id}/retainer, POST .../retainer/transfer-credit — retainer setup and credit transfer (scope: Billing)
Documents
/api/v1/documents · scope: Documents · shared across Process Serving and Investigation cases

An AI agent's tool call can construct a JSON body far more reliably than a multipart form. Upload a file as:

{ "processServingCaseId": 123, "fileName": "affidavit.pdf", "contentBase64": "JVBERi0xLjQK..." }

Exactly one of processServingCaseId/investigationCaseId must be set. Allowed extensions and the 25 MB size cap match every other upload endpoint in this API. Notarized signing-job documents use a separate endpoint under /api/v1/notary/documents with the same base64 convention.

  • GET /api/v1/documents, GET/DELETE .../{id}, GET .../{id}/content (view/download)
  • POST /api/v1/documents (single), POST .../batch (multiple)
  • PUT /api/v1/documents/order — reorder; POST .../{id}/toggle-visible — client visibility
  • POST /api/v1/documents/batch-download — merges selected documents into one PDF
  • POST /api/v1/documents/email — emails documents to a recipient (real email, see Side Effects)
Clients
/api/v1/clients · scope: Clients

Full CRUD: GET /api/v1/clients, GET/PUT/DELETE .../{id}, POST /api/v1/clients. Delete is a hard delete with no guard against existing cases or invoices — this matches the human UI exactly, so a client with related records may fail on a database constraint rather than a friendly error.

Billing
/api/v1/billing/* · scope: Billing · see Side Effects above before using
  • GET/POST /api/v1/billing/invoices, GET/PUT .../{id} (edit blocked once Paid/Void)
  • POST .../{id}/send, POST .../{id}/mark-paid, POST .../{id}/void, GET .../{id}/pdf
  • POST /api/v1/billing/invoices/bill-case-items — bills unbilled investigation time/expenses onto a new invoice or against the case retainer
  • POST /api/v1/billing/invoices/retainer, POST .../topup — retainer/top-up invoices (auto-send a Stripe link inline if configured)
  • GET/POST /api/v1/billing/payouts, GET .../{id}, POST .../{id}/send, GET .../{id}/pdf — no edit/delete, matching the UI
  • GET /api/v1/billing/credit/{clientId} — ledger + running balance
  • POST /api/v1/billing/credit/{clientId}/adjust — the one manual credit adjustment action; every other ledger entry type is system-written as a side effect of an invoice or retainer action
Quotes, Time & Expenses
scopes: Quotes, TimeAndExpense
  • GET /api/v1/ps/quotes, POST .../generate (persists immediately — not a pure calculation), POST .../{id}/save-to-case
  • POST /api/v1/inv/quotes/calculate (pure, no persist), GET/POST/PUT/DELETE /api/v1/inv/quotes
  • GET/POST/PUT/DELETE /api/v1/time-entries — Investigation cases only; requires a userId belonging to the agency (no signed-in user to default to under API-key auth)
  • GET/POST/PUT/DELETE /api/v1/inv/expenses — Investigation-only, optional base64 receipt upload
Signing Jobs & Journal
/api/v1/notary/*
  • GET/POST/PUT/DELETE /api/v1/notary/signing-clients — delete deactivates instead of removing if the client has jobs
  • POST .../{id}/contacts, POST .../{id}/contacts/{contactId}/toggle-active, POST .../{id}/contacts/{contactId}/invite-portal
  • GET/POST/PUT/DELETE /api/v1/notary/signing-jobs — delete cancels instead of removing once notarial acts exist; status changes are validated (see Immutable Records)
  • POST/DELETE /api/v1/notary/signing-jobs/{id}/signers/{signerId?} — ID numbers are encrypted on write, only a masked value is ever returned
  • POST /api/v1/notary/signing-jobs/{id}/notarial-acts — logging an act automatically creates its permanent journal entry in the same request
  • GET /api/v1/notary/journal, GET .../{id}, POST .../{id}/void — read and void only, nothing else, ever
  • GET/POST/DELETE /api/v1/notary/documents, GET .../{id}/content — same base64 upload convention as the agency Documents API
  • GET/POST /api/v1/notary/invoices, POST .../{id}/mark-sent, POST .../{id}/mark-paid, POST .../{id}/void — no payment-processor integration, tracked manually. Void is a new capability with no equivalent in the human UI today.
Errors
What a failure looks like
  • 401 Unauthorized — missing, malformed, or revoked API key.
  • 403 Forbidden — valid key, but missing the required scope, or (Notary endpoints) the agency doesn't have the Notary module enabled.
  • 404 Not Found — the record doesn't exist, or exists but belongs to a different agency than the key's — the API never distinguishes these, to avoid leaking existence across tenants.
  • 400 Bad Request — validation failure. Model-binding errors return ASP.NET Core's standard ValidationProblemDetails shape (an errors object keyed by field name); hand-written checks return a simpler { "error": "..." } body.
  • 429 Too Many Requests — rate limit exceeded (see Rate Limits above).