REST API v1 — Public

Middledoc API & Integrations

Connect Middledoc to n8n, Make, Zapier, Pipedream, or any HTTP client. Use the REST API to manage clients, receive webhooks when events happen, and poll the event log for automation workflows.


Authentication

All API requests are authenticated with a bearer token. Tokens start with mdoc_ and are generated in your dashboard.

Getting your API key

  1. Open Settings → Integrations
  2. Scroll to API & Zapier Inbound
  3. Click Generate API Key
  4. Copy the key immediately — it is shown once and then hashed in storage.

Using the key

Pass the key in the Authorization header on every request:

http
Authorization: Bearer mdoc_your_key_here

Base URL: https://middledoc.com/api/v1

Rate limit: 60 requests per minute per API key

Rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining

Example request

bash
curl https://middledoc.com/api/v1/clients \
  -H "Authorization: Bearer mdoc_your_key_here"

Clients API

Clients are the contacts you collect documents from. You can create, list, and look up clients via the API.

GET/clientsList clients (paginated)
POST/clientsCreate a single client
POST/clients/bulkBulk import up to 100 clients
GET/clients/{id}Get a single client by ID

GET /clients — List clients

Returns a paginated list of all clients in your account. Supports search and pagination query params.

ParamTypeRequiredDescription
pageintegeroptionalPage number (default: 1)
per_pageintegeroptionalResults per page, max 100 (default: 50)
searchstringoptionalFilter by name or email (case-insensitive)
bash
curl "https://middledoc.com/api/v1/clients?page=1&per_page=20&search=alice" \
  -H "Authorization: Bearer mdoc_your_key_here"
json
{
  "data": [
    {
      "id": 42,
      "name": "Alice Johnson",
      "email": "[email protected]",
      "company_name": "Johnson Consulting",
      "phone": "+1 555 000 1234",
      "category": "Tax",
      "labels": ["VIP", "US Client"],
      "created_at": "2026-06-01T09:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 1,
    "total_pages": 1
  }
}

POST /clients — Create a client

Creates a single client. Returns 201 on success. Fires the client.created webhook event.

FieldTypeRequiredDescription
namestringrequiredFull name of the client
emailstringrequiredEmail address (must be unique per account)
company_namestringoptionalCompany or firm name
phonestringoptionalPhone number (any format)
categorystringoptionalCustom category label
labelsstring[]optionalArray of label names to assign; created if missing
bash
curl -X POST https://middledoc.com/api/v1/clients \
  -H "Authorization: Bearer mdoc_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Alice Johnson",
    "email": "[email protected]",
    "company_name": "Johnson Consulting",
    "phone": "+1 555 000 1234",
    "category": "Tax",
    "labels": ["VIP", "US Client"]
  }'
json
{
  "data": {
    "id": 42,
    "name": "Alice Johnson",
    "email": "[email protected]",
    "company_name": "Johnson Consulting",
    "phone": "+1 555 000 1234",
    "category": "Tax",
    "labels": ["VIP", "US Client"],
    "created_at": "2026-07-13T10:00:00Z"
  }
}

POST /clients/bulk — Bulk import

Import up to 100 clients in a single request. Send a JSON array of client objects. Each item is processed independently — rows with validation errors are skipped without failing the entire batch. Duplicate emails are silently skipped.

bash
curl -X POST https://middledoc.com/api/v1/clients/bulk \
  -H "Authorization: Bearer mdoc_your_key_here" \
  -H "Content-Type: application/json" \
  -d '[
    { "name": "Alice Johnson", "email": "[email protected]", "company_name": "Johnson Consulting" },
    { "name": "Bob Smith",     "email": "[email protected]",   "labels": ["Prospect"] },
    { "name": "Bad Row",       "email": "not-an-email" }
  ]'
json
{
  "summary": {
    "total": 3,
    "imported": 2,
    "skipped": 0,
    "errors": 1
  },
  "results": [
    { "index": 0, "status": "imported", "id": 43, "name": "Alice Johnson", "email": "[email protected]" },
    { "index": 1, "status": "imported", "id": 44, "name": "Bob Smith",     "email": "[email protected]" },
    { "index": 2, "status": "error",    "reason": "Invalid email format" }
  ]
}

GET /clients/{id} — Get a client

Returns a single client by numeric ID. Returns 404 with code NOT_FOUND if the client does not exist or belongs to another account.

bash
curl https://middledoc.com/api/v1/clients/42 \
  -H "Authorization: Bearer mdoc_your_key_here"
json
{
  "data": {
    "id": 42,
    "name": "Alice Johnson",
    "email": "[email protected]",
    "company_name": "Johnson Consulting",
    "phone": "+1 555 000 1234",
    "category": "Tax",
    "labels": ["VIP", "US Client"],
    "created_at": "2026-06-01T09:00:00Z"
  }
}

Webhooks

Webhooks let Middledoc push real-time notifications to your server or automation tool when events happen in your account. Register a URL and choose which events to subscribe to. Middledoc delivers a POST request within seconds of each event. Requires https:// URLs. Maximum 20 subscriptions per account.

Webhook endpoints

GET/webhooksList all webhook subscriptions
POST/webhooksCreate a webhook subscription
PATCH/webhooks/{id}Update a subscription (url, events, active)
DELETE/webhooks/{id}Delete a subscription
POST/webhooks/{id}/testSend a test event to the subscription

Creating a subscription

FieldTypeRequiredDescription
urlstringrequiredTarget URL (must start with https://)
eventsstring[]optionalEvent types to subscribe to. Empty array = all events.
descriptionstringoptionalHuman-readable label for this webhook
secretstringoptionalOptional HMAC secret for signature verification
bash
curl -X POST https://middledoc.com/api/v1/webhooks \
  -H "Authorization: Bearer mdoc_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourserver.com/webhook",
    "events": ["client.created", "document.uploaded", "request.completed"],
    "description": "n8n production webhook",
    "secret": "my_signing_secret"
  }'
json
{
  "data": {
    "id": 7,
    "url": "https://yourserver.com/webhook",
    "events": ["client.created", "document.uploaded", "request.completed"],
    "description": "n8n production webhook",
    "is_active": true,
    "created_at": "2026-07-13T10:00:00Z"
  }
}

Event types

EventWhen it fires
client.createdA new client is added to your account
document.uploadedA client uploads documents via the portal
request.completedAll documents accepted — request is complete
request.createdA new document request is created
invoice.paidAn invoice is marked as paid
document.rejectedA document is rejected during review
request.overdueA request passes its deadline
invoice.overdueAn invoice passes its due date

Payload shape

Every webhook delivery is a POST with Content-Type: application/json. The body always follows this shape:

json
{
  "event": "document.uploaded",
  "timestamp": "2026-07-14T10:00:00Z",
  "data": {
    "client_id": 42,
    "client_name": "Alice Johnson",
    "document_name": "bank_statement_june.pdf",
    "request_id": 18
  }
}

HMAC signature verification

When you supply a secret when creating a subscription, Middledoc signs each delivery. The signature is sent in the X-Webhook-Signature header as sha256=<hex>. Compute HMAC-SHA256 of the raw request body using your secret and compare.

javascript
import { createHmac } from 'node:crypto'

function verifyWebhookSignature(rawBody, secret, signatureHeader) {
  // signatureHeader = "sha256=abc123..."
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex')

  // Use a timing-safe comparison
  const a = Buffer.from(expected)
  const b = Buffer.from(signatureHeader)
  if (a.length !== b.length) return false
  return crypto.timingSafeEqual(a, b)
}

// Express example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-webhook-signature']
  if (!verifyWebhookSignature(req.body, process.env.WEBHOOK_SECRET, sig)) {
    return res.status(401).send('Invalid signature')
  }
  const event = JSON.parse(req.body)
  console.log('Received event:', event.event)
  res.sendStatus(200)
})

Events API (polling)

Every action in Middledoc is logged to an immutable event ledger. The Events API lets you poll this log — useful when you cannot expose a public webhook URL or need to replay historical events.

Polling vs. webhooks: Use webhooks when you need real-time delivery and can host a public HTTPS endpoint. Use the Events API when you run workflows on a schedule, are behind a firewall, or want to replay events.

GET /events — Poll the event log

ParamTypeRequiredDescription
typestringoptionalFilter by event type, e.g. document.uploaded
sinceISO 8601optionalReturn events after this timestamp (exclusive)
untilISO 8601optionalReturn events before this timestamp (exclusive)
pageintegeroptionalPage number (default: 1)
per_pageintegeroptionalResults per page, max 100 (default: 50)
bash
# Poll for all events in the last 5 minutes
curl "https://middledoc.com/api/v1/events?since=2026-07-13T10:00:00Z&type=document.uploaded" \
  -H "Authorization: Bearer mdoc_your_key_here"
json
{
  "data": [
    {
      "id": 1001,
      "event_type": "document.uploaded",
      "payload": {
        "client_id": 42,
        "client_name": "Alice Johnson",
        "document_name": "bank_statement_june.pdf"
      },
      "created_at": "2026-07-13T10:02:31Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 50,
    "total": 1,
    "total_pages": 1
  }
}

Poll every 5 minutes — a typical n8n schedule trigger pattern:

javascript
// Store the last-polled timestamp somewhere persistent
const since = lastPolledAt.toISOString()
const url = `https://middledoc.com/api/v1/events?since=${since}`
const res = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.MIDDLEDOC_API_KEY}` },
})
const { data } = await res.json()
// Process data...
lastPolledAt = new Date()

n8n Integration Guide

n8n supports both push (webhook) and pull (scheduled HTTP request) patterns with Middledoc. Use push for real-time automation; use pull when your n8n instance is behind a private network.

Push: Middledoc triggers n8n

  1. 1

    Generate your API key

    In Middledoc: Settings → Integrations → API & Zapier Inbound → Generate. Copy the key — it is only shown once.

  2. 2

    Add a Webhook node in n8n

    Create a new workflow. Add a "Webhook" trigger node. Set Method to POST, note the generated webhook URL.

  3. 3

    Register the URL in Middledoc

    Go to Settings → Integrations → Webhooks → Add webhook. Paste the n8n webhook URL. Select the events you want (e.g. document.uploaded). Optionally add a signing secret and copy it into n8n for verification.

  4. 4

    Build your n8n logic

    Add downstream nodes — Slack, Google Sheets, email, database write, etc. The webhook body contains event, timestamp, and data fields.

Pull: n8n polls Middledoc on a schedule

  1. 1

    Add a Schedule Trigger node

    Set the interval (e.g. every 5 minutes). This fires the workflow on a timer.

  2. 2

    Add an HTTP Request node

    Method: GET. URL: https://middledoc.com/api/v1/events. Add header Authorization: Bearer mdoc_your_key_here. Add query params: since (use an expression referencing your last-run timestamp).

  3. 3

    Process the results

    Add an "Item Lists" or "Split In Batches" node to loop over returned events. Route by event_type using an IF or Switch node.


Zapier Integration Guide

Middledoc integrates with Zapier in both directions: Zapier can receive events from Middledoc (outbound / Trigger) and Zapier can write data back to Middledoc (inbound / Action).

Outbound: Middledoc triggers Zapier

  1. 1

    Create a Zap with "Webhooks by Zapier" as trigger

    Choose Catch Hook. Copy the custom webhook URL Zapier gives you.

  2. 2

    Register the URL in Middledoc

    Go to Settings → Integrations → Zapier and paste the Catch Hook URL. Alternatively, go to Settings → Integrations → Webhooks → Add to register it via the API.

  3. 3

    Test the trigger

    Perform an action in Middledoc (e.g. add a client) and click "Test trigger" in Zapier to confirm the payload arrives.

  4. 4

    Add Zapier action steps

    Connect to Google Sheets, Slack, HubSpot, Airtable, or any other Zapier integration using the event fields (event, timestamp, data.*).

Inbound: Zapier writes to Middledoc

Use Webhooks by Zapier as an Action in any Zap to create clients in Middledoc:

  1. 1

    Add a "Webhooks by Zapier" Action step

    Choose "POST" as the event type.

  2. 2

    Set the URL

    https://middledoc.com/api/v1/clients for a single client, or https://middledoc.com/api/v1/clients/bulk to import multiple.

  3. 3

    Add the Authorization header

    Key: Authorization — Value: Bearer mdoc_your_key_here

  4. 4

    Map the payload

    Set Content-Type to application/json. Map name and email from your trigger step fields.


Error Reference

All error responses are JSON with an error message and a machine-readable code:

json
{ "error": "A client with this email already exists", "code": "CONFLICT" }
HTTPCodeMeaning
401UNAUTHORIZEDMissing or invalid API key
400VALIDATION_ERRORRequest body or params failed validation
409CONFLICTResource already exists (e.g. duplicate email)
404NOT_FOUNDResource not found or not owned by your account
429RATE_LIMITEDExceeded 60 req/min — back off and retry
500INTERNAL_ERRORUnexpected server error — contact support