Dieser Artikel liegt noch nicht auf Deutsch vor und wird unten auf Englisch angezeigt.
On this pageTwo kinds of key
There is no separate sandbox to drift out of date. A test key works against your real organisation and real master data, validates every write exactly as production would, and stores nothing.
Two kinds of key
| Live key | Test key | |
|---|---|---|
| Prefix | dpp_live_… | dpp_test_… |
| Reads | Your data | Your data |
| Writes | Stored | Dry run: validated, never stored |
| Rate limit | 600 requests / minute | 60 requests / minute |
X-DPP-Mode response header | live | test |
| Webhooks | Sent for real changes | Never sent |
| Usage | Counted | Never counted |
Create either kind in the app under Organisation → API keys. Both carry the same read / write scopes, and both need a plan that includes API access (Connected).
What a dry run checks
Everything a real write would check, in the same order: authentication and scope, the request schema, that every referenced id (a model, a passport) exists in your organisation, and the business rules. A request that would fail live fails the same way in a dry run.
A request that would succeed answers 200 with a body that says so, and nothing is written — no model, no passport, no event, no audit entry, no webhook:
curl -X POST "https://dppapi-production.up.railway.app/v1/items/batch" \-H "Authorization: Bearer $DPP_API_KEY" \-H "Content-Type: application/json" \-d '{"modelId": "cm1q8x2k40003l70f9a8s7d6e","count": 100,"serialPrefix": "C48-2027-","placeOfManufacture": "Stuttgart, Germany"}'{"dry_run": true,"valid": true,"operation": "POST /items/batch","mode": "test","checks": ["authentication", "scope", "plan", "schema", "ownership"],"message": "Validated only. Nothing was stored, no webhook was sent, and no usage was counted."}dry_run, valid and operation are always present; the response also carries the header X-DPP-Dry-Run: true. Where the outcome depends on rules that can be checked without writing, the answer says what would happen:
| Operation | Extra in the dry-run answer |
|---|---|
POST /models | completeness: whether the new model would be complete, and the model-level fields it would still owe. |
POST /passports/{id}/publish | would_publish and blockers. |
POST /passports/publish | results: per id, would_publish and blockers, or error: "not_found". |
An id that is not in your organisation answers 404 with not_found, exactly as the real write would refuse it; an invalid body answers 400 with details.
One dry run with a live key
A live key can dry-run a single write by adding X-DPP-Dry-Run: true. Use it for a pre-flight check in production — "would this batch publish?" — without a second key.
curl -X POST "https://dppapi-production.up.railway.app/v1/passports/publish" \-H "Authorization: Bearer $DPP_API_KEY" \-H "Content-Type: application/json" \-H "X-DPP-Dry-Run: true" \-d '{"ids": ["cm1q8y0bq0006l70fx9y8z7w6"]}'Know which mode you are in
Every response carries X-DPP-Mode: live or X-DPP-Mode: test, and GET /me reports the key. Assert it at start-up so a test key can never be deployed to production by mistake — a job that "succeeds" in test mode has created nothing.
const res = await fetch('https://dppapi-production.up.railway.app/v1/me', {headers: { Authorization: `Bearer ${process.env.DPP_API_KEY}` },});if (process.env.NODE_ENV === 'production' && res.headers.get('x-dpp-mode') !== 'live') {throw new Error('Refusing to start: production must use a live DPP key.');}A testing strategy that works
- 1Develop against a test key. Your code sees real models and passports, and nothing you send can create or change one.
- 2Run CI with a test key. Contract tests exercise every write path; failures surface as the same 400/404/422 errors production would give.
- 3Pre-flight in production with
X-DPP-Dry-Run: truebefore a large batch or publish. - 4Go live by swapping the key. No code changes, no base-URL change.