Skip to content

Launching soon: search & applications go live on September 30, 2026. Set up your profile now.

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_enabled consent must be set, or every call fails closed with 403 CONSENT_REQUIRED. Before the public marketplace launch these endpoints additionally return 403 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. A pk_ key is rejected with 403 INSUFFICIENT_SCOPE (applicant PII must never reach a browser key).
  • Scope applications:read; applications:write to set status/tags.
  • Your organization's api_applicant_access_enabled consent set (otherwise every call fails closed with 403 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, rejected only. new and withdrawn cannot be set; withdrawn applications are read-only.
  • An illegal transition (e.g. accepted → new) returns 409 INVALID_STATUS_TRANSITION.
  • tags use set semantics (max 20 tags, ≤40 chars each).
  • The body must be strict with 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

CodeHTTPWhat happenedFix
CONSENT_REQUIRED403Org consent api_applicant_access_enabled not setEnable applicant-data access in Settings.
INSUFFICIENT_SCOPE403pk_ key or missing scopeUse a secret key with the right scope.
NOT_FOUND404Resource not found or not in your orgCheck the ID and ownership.
INVALID_STATUS_TRANSITION409Illegal status change / withdrawn is read-onlyOnly shortlisted/accepted/rejected.
VALIDATION_ERROR400Empty body, unknown field, or new/withdrawnSend 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 403 gracefully.
  • Audit-friendly writes. Each status change is logged with your key; use one key per integration so the audit trail is meaningful.

Next steps