Protocol

deep.rent Integrator API

Public reference for integrating with deep.rent protocol data via API keys and webhooks.

Base URL: https://api.deep.rent (replace with your environment, e.g. https://api-dev.deep.rent).


Overview

Capability Auth Endpoint
List protocols API key GET /external/protocols
Download protocol PDF API key GET /external/protocols/:id/pdf
Manage API keys Session (org owner) /api/api-keys
Manage webhook subscriptions Session /api/webhooks
Receive protocol events Your HTTPS endpoint (outbound POST from deep.rent)

The API-key surface is read-only: you can list protocols and download PDFs. Creating, updating, or deleting protocols is not available via API key.

┌─────────────────────────────────────────────────────────────────┐
│  Setup (once, session auth)                                     │
│    POST /api/api-keys        → create API key (save key!)       │
│    POST /api/webhooks        → register callback URL (optional) │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Runtime (API key)                                              │
│    GET /external/protocols              → sync / discover ids    │
│    GET /external/protocols/:id/pdf      → download PDF          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Events (optional, outbound webhook)                            │
│    POST your-url  { event, orgId, protocol }  → react to change │
│    GET /external/protocols/:id/pdf          → fetch PDF if needed│
└─────────────────────────────────────────────────────────────────┘

Webhooks deliver metadata only (protocol snapshot JSON). The PDF is always fetched in a separate API call.


Authentication

API key (/external/*)

Send your API key on every external request:

x-api-key: YOUR_API_KEY

Or:

Authorization: Bearer YOUR_API_KEY
  • Each key is bound to one organization (set at creation). You cannot override org with x-org-id.
  • The key owner must be a member of that organization.
  • Keys are created by an organization owner via POST /api/api-keys. The raw key is shown once — store it securely.

Session (/api/api-keys, /api/webhooks)

Webhook and API-key management require a normal deep.rent login (session cookie or bearer token).

For org-scoped operations, send:

x-org-id: YOUR_ORG_ID

Optional when the session already has an active organization.


API keys

Organization owners create and revoke keys for integrations.

Create

POST /api/api-keys
Content-Type: application/json
Cookie: <session>
x-org-id: <org-id>

{
  "name": "CRM sync",
  "description": "Production integration"
}

Response 201:

{
  "id": "…",
  "name": "CRM sync",
  "description": "Production integration",
  "key": "…",
  "createdAt": "2025-06-14T08:00:00.000Z"
}

List

GET /api/api-keys
Cookie: <session>
x-org-id: <org-id>

Response 200:

{
  "apiKeys": [
    {
      "id": "…",
      "name": "CRM sync",
      "description": "Production integration",
      "createdAt": "…",
      "updatedAt": "…",
      "enabled": true
    }
  ]
}

Get one

GET /api/api-keys/:id
Cookie: <session>
x-org-id: <org-id>

Revoke

DELETE /api/api-keys/:id
Cookie: <session>
x-org-id: <org-id>

Response 204 — no body.

Status Meaning
401 Not authenticated
403 Not an org owner
404 Key not found

List protocols

Returns protocol snapshots for the organization bound to your API key.

GET /external/protocols
x-api-key: YOUR_API_KEY

Query parameters

Parameter Default Max Description
limit 50 200 Page size
cursorTs ISO 8601 timestamp (use with cursorId)
cursorId Protocol id; with cursorTs, returns older rows
includeDeleted false Include soft-deleted protocols

Example

curl -s "https://api.deep.rent/external/protocols?limit=20" \
  -H "x-api-key: YOUR_API_KEY"

Success 200

{
  "success": true,
  "count": 1,
  "userId": "…",
  "orgId": "…",
  "protocols": [ … ],
  "hasMore": false,
  "nextCursor": null
}

When hasMore is true:

"nextCursor": {
  "cursorTs": "2025-01-15T10:30:00Z",
  "cursorId": "Jwlo-Qe8a-3dSn-OQ-w"
}

Pass both values as query params on the next request.

Pagination

curl "https://api.deep.rent/external/protocols?limit=50&cursorTs=2025-01-15T10:30:00Z&cursorId=Jwlo-Qe8a-3dSn-OQ-w" \
  -H "x-api-key: YOUR_API_KEY"

Soft-deleted protocols

Default: active protocols only. With includeDeleted=true, check meta.deletedAt — non-null means soft-deleted. May include meta.deletedBy and meta.deleteReason.

Each row includes protocol.link (internal storage reference). To download the PDF, call GET /external/protocols/:id/pdf with the top-level id.

Status Meaning
401 Missing or invalid API key
500 Server error

Download protocol PDF

Returns a short-lived presigned URL for the protocol PDF file.

  • If the PDF already exists in storage → immediate response (generated: false).
  • If not → server builds the PDF from protocol data, stores it, then returns the URL (generated: true). First request can take longer (up to ~60 seconds).

Use the protocol id from the list response or webhook payload — not protocol.link.

Endpoint

GET /external/protocols/:id/pdf
x-api-key: YOUR_API_KEY

Protocol id format: at least 6 characters; letters, digits, _, and - only (e.g. Jwlo-Qe8a-3dSn-OQ-w).

Example

# 1. Get presigned URL
curl -s "https://api.deep.rent/external/protocols/Jwlo-Qe8a-3dSn-OQ-w/pdf" \
  -H "x-api-key: YOUR_API_KEY"

# 2. Download PDF (no API key on this URL)
curl -L -o protocol.pdf "PRESIGNED_URL_FROM_RESPONSE"

Success 200

{
  "success": true,
  "protocolId": "Jwlo-Qe8a-3dSn-OQ-w",
  "orgId": "…",
  "fileName": "protocol-Jwlo-Qe8a-3dSn-OQ-w.pdf",
  "url": "https://…",
  "expiresIn": 900,
  "expiresAt": "2025-06-14T09:15:00.000Z",
  "generated": false
}
Field Description
url Presigned GET URL — valid for a direct HTTP download
expiresIn Seconds until expiry (900 = 15 minutes)
expiresAt ISO 8601 expiry time
generated true if PDF was created on this request; false if it already existed
fileName Suggested filename when saving locally

Notes:

  • Request a fresh presigned URL when the previous one expires; do not cache URLs beyond expiresAt.
  • The presigned URL does not require your API key.
  • Repeat calls after the PDF exists are fast and return generated: false.
  • Allow up to ~60 seconds for the API response when generated may be true (first download for a protocol).

End-to-end download (bash)

Two steps: (1) get presigned URL from deep.rent, (2) download the file from that URL.

API_KEY="YOUR_API_KEY"
PROTOCOL_ID="Jwlo-Qe8a-3dSn-OQ-w"
BASE="https://api.deep.rent"

# Step 1 — presigned URL (requires API key)
RESP=$(curl -sS "${BASE}/external/protocols/${PROTOCOL_ID}/pdf" \
  -H "x-api-key: ${API_KEY}")

URL=$(echo "$RESP" | jq -r '.url')
FILE=$(echo "$RESP" | jq -r '.fileName')
GENERATED=$(echo "$RESP" | jq -r '.generated')

echo "generated=${GENERATED} expiresAt=$(echo "$RESP" | jq -r '.expiresAt')"

# Step 2 — download PDF (no API key on presigned URL)
curl -L -o "$FILE" "$URL"
echo "Saved ${FILE}"

End-to-end download (Node.js)

const API_KEY = process.env.DEEP_RENT_API_KEY;
const BASE = 'https://api.deep.rent';
const protocolId = 'Jwlo-Qe8a-3dSn-OQ-w';

const metaRes = await fetch(`${BASE}/external/protocols/${protocolId}/pdf`, {
  headers: { 'x-api-key': API_KEY },
});
if (!metaRes.ok) throw new Error(`PDF meta failed: ${metaRes.status}`);

const { url, fileName } = await metaRes.json();

const pdfRes = await fetch(url);
if (!pdfRes.ok) throw new Error(`PDF download failed: ${pdfRes.status}`);

const buffer = Buffer.from(await pdfRes.arrayBuffer());
await fs.promises.writeFile(fileName, buffer);

After a webhook (protocol.generated)

Webhooks deliver metadata only — no PDF attachment. Typical handler:

1. Receive POST { event, orgId, protocol }
2. Read protocol.id from the payload
3. GET /external/protocols/:id/pdf  (with API key)
4. GET the presigned url           (no API key)
5. Store or forward the PDF in your system

Example: react to a new protocol and archive the PDF:

# protocol.id from webhook JSON, e.g. Jwlo-Qe8a-3dSn-OQ-w
PROTOCOL_ID="$1"
curl -sS "https://api.deep.rent/external/protocols/${PROTOCOL_ID}/pdf" \
  -H "x-api-key: ${API_KEY}" \
  | jq -r '.url' \
  | xargs curl -L -o "protocol-${PROTOCOL_ID}.pdf"

Batch sync (list → download)

# List recent protocols, then download each PDF
curl -sS "https://api.deep.rent/external/protocols?limit=50" \
  -H "x-api-key: ${API_KEY}" \
  | jq -r '.protocols[].id' \
  | while read -r ID; do
      curl -sS "https://api.deep.rent/external/protocols/${ID}/pdf" \
        -H "x-api-key: ${API_KEY}" \
        | jq -r '.url' \
        | xargs curl -L -o "protocol-${ID}.pdf"
    done

Skip protocols where meta.deletedAt is set unless you explicitly need archived copies.

Status Meaning
400 Invalid protocol id, or invalid storage configuration
401 Missing or invalid API key
404 Protocol not found / no access, or source data missing in storage
500 PDF generation or presign failed

Webhook subscriptions

Register HTTPS URLs to receive protocol lifecycle notifications. Management requires session auth.

Base path: /api/webhooks

Events

Event When it fires
protocol.generated Protocol created
protocol.deleted Protocol soft-deleted

List subscriptions

GET /api/webhooks
Cookie: <session>
x-org-id: <org-id>

Response 200: array of:

Field Type Description
id string Subscription id
orgId string Organization id
name string | null Optional label
event string protocol.generated or protocol.deleted
url string Your callback URL
active boolean Enabled/disabled
createdAt string ISO 8601

The signing secret is never returned by these endpoints.

Create

POST /api/webhooks
Content-Type: application/json
Cookie: <session>
x-org-id: <org-id>

{
  "name": "CRM sync",
  "event": "protocol.generated",
  "url": "https://your-server.example.com/hooks/deep-rent",
  "active": true
}
Field Required Description
event yes protocol.generated or protocol.deleted
url yes HTTPS callback URL
name no Display name
active no Default true

Response 201: created subscription (no secret).

Update

PUT /api/webhooks/:id
Content-Type: application/json

{ "url": "https://new-url.example.com/hooks", "active": false }

Partial update — send only changed fields. Response 200 or 404.

Delete

DELETE /api/webhooks/:id

Response 204 or 404.

Allowed events

GET /api/webhooks/events
{ "events": ["protocol.generated", "protocol.deleted"] }

Outbound webhook delivery

When a subscribed event occurs, deep.rent POSTs to your registered url.

Rollout note: Subscription management is available now. Outbound delivery and signature verification are being rolled out — confirm behaviour in your environment before production reliance.

Payload

{
  "event": "protocol.generated",
  "orgId": "<organization-id>",
  "protocol": { … }
}

For deletions: "event": "protocol.deleted". The protocol object matches the protocol snapshot below (including deletion fields when applicable).

PDF is not attached. After receiving a webhook, call GET /external/protocols/:id/pdf if you need the document.

Protocol snapshot

Structure used in list responses, webhooks, and the protocol field above:

Field Description
id Protocol id — use for PDF download
object id, category, type, name, address, addressId
protocol link, cause, store_kb, createdAt, documentCosts, retentionUntil, landlordConfirmation
contacts id, email, firstName, lastName, salutation, resolvedSalutation, companyName, personTitle, role
owner orgId, userId, userName
store Storage region identifier
storage_kb Storage usage (string)
meta updatedAt, version, tags, deletedAt, deletedBy, deleteReason, legalHold, legalHoldReason

Example

{
  "id": "Jwlo-Qe8a-3dSn-OQ-w",
  "object": {
    "id": "4IdD-3NdM-TrSm-dKGb",
    "category": "REALESTATE",
    "type": "REALESTATE_RESIDENTIAL_APARTMENT",
    "name": "Sample object",
    "address": {
      "addressLine1": "Example Street 1",
      "city": "Berlin",
      "postalCode": "10115",
      "country": "Germany",
      "countryCode": "DE"
    },
    "addressId": "…"
  },
  "protocol": {
    "link": "eu-deepstore-ce-2/protocol/Jwlo-Qe8a-3dSn-OQ-w",
    "cause": "Handover",
    "store_kb": 54,
    "createdAt": "2025-01-15T10:30:00Z",
    "documentCosts": false,
    "retentionUntil": "2030-01-15T10:30:00Z",
    "landlordConfirmation": true
  },
  "contacts": [
    {
      "id": "…",
      "email": "[email protected]",
      "firstName": "Max",
      "lastName": "Mustermann",
      "salutation": "MR",
      "resolvedSalutation": "Herr",
      "companyName": null,
      "personTitle": null,
      "role": "RENTER"
    }
  ],
  "owner": {
    "orgId": "…",
    "userId": "…",
    "userName": "…"
  },
  "store": "eu-deepstore-ce-2",
  "storage_kb": "54",
  "meta": {
    "tags": ["handover"],
    "version": 1,
    "deletedAt": null,
    "deletedBy": null,
    "legalHold": false,
    "updatedAt": "2025-01-15T10:35:00Z",
    "deleteReason": null,
    "legalHoldReason": null
  }
}

Signature verification

A signing secret is generated server-side when a subscription is created. Header names and verification steps will be documented when outbound delivery is generally available.


Quick reference

# Machine-to-machine (API key)
GET  /external/protocols
GET  /external/protocols/:id/pdf

# API keys (session, org owner)
POST   /api/api-keys
GET    /api/api-keys
GET    /api/api-keys/:id
DELETE /api/api-keys/:id

# Webhooks (session)
GET    /api/webhooks
POST   /api/webhooks
PUT    /api/webhooks/:id
DELETE /api/webhooks/:id
GET    /api/webhooks/events

Support

For integration questions or early webhook delivery access, contact your deep.rent account representative.