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.
🔴 PII-critical. The applications API (Plan 59) is generally available and documented in the public reference — no feature flag gates it. Access is consent-gated: your organization's
api_applicant_access_enabledconsent must be set, or every call fails closed with403 CONSENT_REQUIRED. Before the public marketplace launch these endpoints additionally return403 LAUNCH_PENDING. 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. - Your organization's
api_applicant_access_enabledconsent set (otherwise every call fails closed with403 CONSENT_REQUIRED).
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 | Resource not found or not in your org | Check the ID and 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.