Skip to content

Open Beta – help us test! All listings are examples only.

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 consent api_applicant_access_enabled (off → 403 CONSENT_REQUIRED, fail-closed). Both must be enabled before any call succeeds. The operations are x-internal and 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. 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.
  • APPLICATIONS_API_ENABLED on, and your organization's api_applicant_access_enabled consent 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, 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_FOUND404Flag off, or resource not in your orgConfirm the flag; check 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