# DocJacket API integration guide

Recommended call sequences for the two ways to integrate. Every endpoint named here is
a real operation in the canonical specification at https://api.docjacket.com/openapi.json;
for parameters, schemas, and error codes, read that or
[/api-reference.md](https://api.docjacket.com/api-reference.md).

- **Base URL:** `https://api.docjacket.com`
- **Auth:** `Authorization: Bearer <key>` on every request
- **Interactive reference:** https://api.docjacket.com/reference
- **Concepts and model:** https://api.docjacket.com/llms-full.txt

---

## Organization API integration

For building against a single organization — a CRM sync, an internal dashboard, a
back-office automation.

### 1. Create an organization-scoped key

In the DocJacket app: **Settings -> Advanced -> API Keys**. Grant only the scopes you need
— `read` for GETs, `draft` for low-risk writes, `actions` for anything that sends or
deletes. The key begins `mcp_at_`.

### 2. Verify authentication

```
GET /api/v1/health
```

Returns `ok`, your `organizationId`, and the `scopes` the key carries. If this fails,
nothing else will work — fix it before continuing.

### 3. Inspect available operations

```
GET /api/v1/catalog
```

Every operation, with its method, path, required scope, and a `callable` flag computed
against *your* key. This is the authoritative answer to "what can this key do" — more
reliable than inferring it from scope names.

### 4. List or create transactions

```
GET  /api/v1/transactions?q=&status=&page=1&pageSize=25
GET  /api/v1/transactions/{transactionId}
GET  /api/v1/transactions/resolve?...
```

Use `/transactions/resolve` when you have a property reference (an address) rather
than an identifier — it returns ranked matches instead of guessing. Prefer resolving
over creating a duplicate deal.

### 5. Upload a contract

```
POST /api/v1/documents/upload-url
```

Returns a presigned PUT URL; upload the PDF directly to it. For small files you can
instead POST the base64 body to `/api/v1/documents`, which uploads and starts
extraction in one call.

### 6. Start extraction

```
POST /api/v1/extractions
```

Body references the completed upload. For a document already in DocJacket, use
`POST /api/v1/documents/{documentId}/extract` instead.

### 7. Poll extraction status, or receive a webhook

```
GET /api/v1/extractions/{jobId}
```

Better: subscribe to `extraction.completed` (step 10) and skip polling entirely. If
you must poll, back off — extraction takes tens of seconds, and the read bucket is 300
calls per minute.

### 8. Review citations and warnings

The extraction result carries per-field confidence and citations back to the source
document, plus warnings for low-confidence or conflicting fields.

**Extraction is a proposal, not a commit.** Put a human in front of anything
irreversible. Do not auto-send client communication off an unreviewed extraction.

Use `GET /api/v1/extractions/{jobId}/source-url` for a short-lived URL to show the
source PDF next to the extracted values.

### 9. Apply the extraction to a transaction

```
POST /api/v1/transactions
```

Omit `transactionId` to create a new transaction from the extraction; include it to
enrich an existing one. Then attach the rest of the deal:

```
POST  /api/v1/transactions/{transactionId}/key-dates      # batch
PATCH /api/v1/transactions/{transactionId}/key-dates/{keyDateType}
POST  /api/v1/transactions/{transactionId}/tasks
POST  /api/v1/transactions/{transactionId}/checklist      # apply a template
POST  /api/v1/transactions/{transactionId}/contacts
```

Read `GET /api/v1/transaction-statuses` before writing a status — the vocabulary is
per organization, not a fixed enum. Changing status with
`PATCH /api/v1/transactions/{transactionId}/status` runs the same cascade as the web
app and may create tasks and shift dates.

### 10. Subscribe to transaction events

```
GET  /api/v1/webhook-events        # the available types
POST /api/v1/webhooks              # create the subscription
POST /api/v1/webhooks/{id}/test    # send yourself a sample delivery
```

Verify `X-Webhook-Signature` (`sha256=<hex>`, HMAC-SHA256 over the raw body with your
subscription secret) before trusting a delivery, and deduplicate on
`X-Webhook-Event-Id` — retries reuse it.

### 11. Monitor

```
GET /api/v1/usage
```

Call volume, daily breakdown, top operations, and error rate for your key.

---

## OAuth integration

For an application acting on behalf of many users — each authorizing their own
DocJacket account. Everything above stays true: the token reaches the same
organization-scoped operations, with the same scopes. Only how you obtain it differs.

Reach for this instead of `mcp_at_` keys as soon as you serve more than a handful of
accounts, since a key would otherwise have to be minted by hand for each one.

### 1. Discover the endpoints

```
GET https://app.docjacket.com/.well-known/oauth-authorization-server
```

Read the endpoints from this document (RFC 8414) rather than hardcoding them. It
advertises `authorization_endpoint`, `token_endpoint`, `registration_endpoint`, the
supported scopes, `code_challenge_methods_supported: ["S256"]`, and
`token_endpoint_auth_methods_supported: ["none"]`.

### 2. Register your client, once

```
POST /oauth/register
{"client_name": "Your App", "redirect_uris": ["https://your.app/callback"]}
```

Dynamic client registration (RFC 7591). Returns a `client_id`. Up to 5 redirect URIs;
they are matched exactly at authorization time, so register the precise callback — a
trailing-slash difference is a different URI.

There is no client secret. This is a public client (`token_endpoint_auth_method:
"none"`), so **PKCE is mandatory**, not optional.

### 3. Send the user to authorize

```
GET /oauth/authorize
    ?response_type=code
    &client_id=<your client_id>
    &redirect_uri=<your registered callback>
    &code_challenge=<S256 challenge>
    &code_challenge_method=S256
    &scope=read draft actions
```

Request the least you need — the user sees the scopes described in plain language on
the approval screen, and a narrower request reads better. `S256` is the only accepted
challenge method.

### 4. Exchange the code

```
POST /oauth/token
grant_type=authorization_code&code=<code>&code_verifier=<verifier>
    &client_id=<client_id>&redirect_uri=<callback>
```

The authorization code is valid for **60 seconds** and is single-use.

You receive `access_token`, `token_type`, `expires_in`, `refresh_token`, and `scope`.
The access token is a JWT whose claims include `sub` (the user), `org_id` (the
organization — **key your storage on this**), `client_id`, and `scope`, plus
`org_name` and `name` where they are set, so you can show a real account and person
rather than an identifier.

### 5. Refresh, and the one rule that will bite you

Access tokens last **1 hour** (`expires_in: 3600`). Refresh tokens last **90 days**
and **rotate on every use** — each refresh returns a new refresh token and retires
the one you sent.

If an already-used refresh token is presented again, we treat it as a stolen-token
replay (OAuth 2.1 reuse detection) and revoke **the entire rotation family** — every
live token for that user and client, including the one legitimately issued moments
earlier. The user must re-authorize.

In practice: store the new refresh token atomically before using it, and never retry
a failed refresh with the old one. A naive retry-on-timeout will disconnect your
user.

### 6. Expect revocation at any moment

A token can stop working before it expires, and your integration should treat a `401`
as "this connection needs re-authorization" rather than as a transient error. Two
causes:

- The user disconnects your application from their DocJacket account, which kills the
  refresh chain.
- The user is removed from the organization. Membership is re-read on **every** call
  rather than trusted from the token, so access stops immediately — not at the end of
  the token's hour.

### 7. Webhooks per connected account

An OAuth token manages that account's webhook subscriptions directly — no separate
credential. `read` lists event types and subscriptions; `actions` creates, updates,
tests, and rotates the signing secret. Use this rather than polling.

### 8. Testing

There is no separate sandbox environment, deliberately: a parallel environment drifts
out of step with the real one. Register a second client pointed at your staging
callback and use a DocJacket account you control as the integration target.

---

## Partner / OEM integration

For white-label partners provisioning DocJacket for their own customers. Requires a
reseller key (`rsk_`).

### 1. Obtain a reseller-scoped credential

Mint one yourself in the partner console, under **API keys** — name the key, tick the
permissions it needs, and copy the value when it appears. We store only a hash, so the
value is shown once and cannot be recovered later; issue a separate key per system you
integrate, so any one can be revoked without disturbing the rest.

The key begins `rsk_` and is accepted **only** on `/api/v1/orgs/*`. An organization key
or OAuth token is rejected there, and a reseller key is rejected everywhere else.

A reseller key never reads deal data. To act inside a provisioned organization, use an
organization key or an OAuth token for that organization.

### 2. Provision an organization

```
POST /api/v1/orgs
GET  /api/v1/orgs
GET  /api/v1/orgs/{orgId}
```

Each provisioned organization is a separate tenant.

### 3. Add users

```
POST   /api/v1/orgs/{orgId}/users
DELETE /api/v1/orgs/{orgId}/users/{userId}
```

### 4. Create an organization-scoped credential where appropriate

The reseller key is a **control plane** credential — provision, seat, and read
entitlements. It cannot read the transactions, documents, or communications inside a
provisioned organization.

To act on deal data, use an organization key issued for that specific organization,
and switch credentials when you switch tenants.

### 5. Perform transaction operations within the correct tenant

Use the organization tier above, with that organization's key. Everything in the
organization section applies unchanged.

### 6. Subscribe to webhooks

Webhook subscriptions are per organization, created with that organization's key. The
envelope carries `organizationId` — route on it, and never merge deliveries from
different organizations into one context.

### 7. Track usage

```
GET /api/v1/orgs/{orgId}/entitlements   # plan, seats, AI fair-use pool
GET /api/v1/orgs/{orgId}/summary        # active deals, tasks due, recent activity
```

### 8. Never infer or bypass tenant boundaries

- Identifiers are not capabilities. Holding an identifier does not grant access to it.
- An organization you do not own returns **404, never 403** — ownership is never
  disclosed. Do not use 404 to probe for existence.
- Never present one organization's data in another organization's context, and never
  reuse a credential across tenants.

---

## Conventions that apply to every call

| Topic | Behavior |
|---|---|
| Auth | `Authorization: Bearer <key>` on every request |
| Errors | `{ "error": { "code", "message", "fields"? } }` — branch on `code`, never on `message` |
| Cross-tenant | Returns `404`, never `403` |
| Rate limits | Per key per minute: 300 reads, 60 writes. `429` + `Retry-After: 60` |
| Retries | Send `Idempotency-Key` on sends; the spec marks which operations honor it |
| Compatibility | Additive — ignore unknown response fields, do not depend on property order |
