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.
Recommended integration flow
┌─────────────────────────────────────────────────────────────────┐
│ 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.
Important: protocol.link is not a PDF URL
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
generatedmay betrue(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.