Pull applications into your ATS
Read tenant applications into your own applicant-tracking system and set status decisions back — within strict PII and consent boundaries.
This guide shows how to read incoming applications for your listings into your own ATS, interpret the coarse matching signal, and write status decisions back to WOHNO — all within strict data-minimization boundaries.
The problem this solves: your team triages applicants in an existing ATS. You want WOHNO applications to land there and your shortlist/accept/reject decisions to flow back.
Beta / dark-shipped · 🔴 PII-critical. The applications API (Plan 59) sits behind a double gate: the feature flag
APPLICATIONS_API_ENABLED(off →404) and the per-organization consentapi_applicant_access_enabled(off →403 CONSENT_REQUIRED, fail-closed). Both must be enabled before any call succeeds. The operations arex-internaland not in the public reference. Enabling consent activates a data-processing arrangement — coordinate with your WOHNO contact and your DPO.
Prerequisites
- A secret key (
sk_live_…) — applications are sk-only. Apk_key is rejected with403 INSUFFICIENT_SCOPE(applicant PII must never reach a browser key). - Scope
applications:read;applications:writeto set status/tags. APPLICATIONS_API_ENABLEDon, and your organization'sapi_applicant_access_enabledconsent set.
What you do (and do not) receive
Responses are the reduced ApplicationPublicDto. By design (GDPR data
minimization) it never contains: income in €, SCHUFA score/status, guarantor
names, clear PII (name / address / phone / email), document or dossier URLs, the
cover letter, internal score weights/thresholds, or the knockout detail list.
Re-identification happens only through applicant_ref — an org-local pseudonym —
never through a global user ID. Every read and every status write is audited
against your api_key_id.
Step 1 — List applications for a listing
curl "https://wohno.de/api/v1/listings/9f1c2a3b-1111-2222-3333-444455556666/applications?status=new&page=1&per_page=50" \
-H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"Response (200, offset-paginated):
{
"data": [
{
"id": "app_7d8e9f01",
"applicant_ref": "ref_3a2b1c",
"status": "new",
"score": "high",
"tags": [],
"created_at": "2026-06-02T09:15:00.000Z"
}
],
"meta": {
"total": 12,
"page": 1,
"perPage": 50,
"hasMore": false,
"timestamp": "2026-06-04T10:30:00.000Z"
}
}per_page (max 100) caps the size of each PII read. A listing that belongs to
another organization returns an empty list rather than leaking its existence.
Step 2 — Interpret the score
score is a coarse band (not the internal numeric score, which is never
exposed). Treat it as a triage hint — high / medium / low — and apply your
own ranking on top. Do not attempt to reconstruct the underlying model; weights,
thresholds and knockout reasons are intentionally withheld.
Step 3 — Read a single application
curl "https://wohno.de/api/v1/applications/app_7d8e9f01" \
-H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"Unknown or other-org IDs return 404 NOT_FOUND (no existence leak).
Step 4 — Write a status decision back
Set a decision and/or tags. The write runs through the application state machine,
emits application.* events, and is audited:
curl -X PATCH https://wohno.de/api/v1/applications/app_7d8e9f01 \
-H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "status": "shortlisted", "tags": ["viewing-invited"] }'- Settable statuses:
shortlisted,accepted,rejectedonly.newandwithdrawncannot be set;withdrawnapplications are read-only. - An illegal transition (e.g.
accepted → new) returns409 INVALID_STATUS_TRANSITION. tagsuse set semantics (max 20 tags, ≤40 chars each).- The body must be
strictwith at least one property; unknown fields →400 VALIDATION_ERROR.
status: accepted emits application.accepted; rejected emits
application.rejected. Subscribe to these via
webhooks to keep both
systems consistent.
Error handling
| Code | HTTP | What happened | Fix |
|---|---|---|---|
CONSENT_REQUIRED | 403 | Org consent api_applicant_access_enabled not set | Enable applicant-data access in Settings. |
INSUFFICIENT_SCOPE | 403 | pk_ key or missing scope | Use a secret key with the right scope. |
NOT_FOUND | 404 | Flag off, or resource not in your org | Confirm the flag; check ownership. |
INVALID_STATUS_TRANSITION | 409 | Illegal status change / withdrawn is read-only | Only shortlisted/accepted/rejected. |
VALIDATION_ERROR | 400 | Empty body, unknown field, or new/withdrawn | Send at least one allowed field. |
See the conventions reference.
Best practices
- Store the pseudonym, not raw PII. Key your ATS records on
applicant_ref. - Honor the consent. If consent is revoked, calls fail closed — handle the
403gracefully. - Audit-friendly writes. Each status change is logged with your key; use one key per integration so the audit trail is meaningful.
Next steps
- React to events with webhooks — get
application.*events pushed. - Authentication guide — scopes and secret-key handling.
- API Reference —
ApplicationPublicDtofields.