Skip to documentation
View Markdown

Customer-hosted applications

On this page
  1. REST operations and key permissions
  2. Build a customer-hosted application flow
  3. 1. Load the current form before collecting an application.
  4. 2. Keep each credential in its own role.
  5. 3. Complete the required identity handoff, then submit once.
  6. 4. Read the receipt before deciding what to retry.
  7. 5. Upload declared bytes and finalize.
  8. Recover by error code, without creating a second application.
  9. Keep private data out of public caches

Collect an application, complete required identity checks and recover document uploads safely.

REST operations and key permissions

TaskKey permissionsStart here
Use the customer-server application intake budget or hosted eID handoffwrite:applications on a separate server keyPOST /api/v1/public/units/{slug}/application-eid/handoffs

Build a customer-hosted application flow

Use the versioned REST intake when you host an application form on your own website. Start from the confirmed API origin in the connection guide. This flow is separate from landlord application reads and from MCP tools; a company key never stands in for the applicant's identity or consent.

1. Load the current form before collecting an application.

Read GET /api/v1/public/units/{slug}/application-form?language=sv (also en or es). Keep the returned unitSlug, listingPeriodId and opaque formVersion. Render the published inputSchema, conditional rules, policies.confirmations, document requirements and identityGate; do not recreate them from assumptions. Link the supplied privacy/terms paths through the confirmed Homito web-app origin, not the API origin. Check canApply and unavailableReason; reading a form does not create or reopen an application round.

Use POST /api/v1/public/units/{slug}/application-form/validate for draft feedback. Draft validation checks the shared field rules, confirmations and declared document metadata against a read-only form snapshot. It creates no application or consent audit, needs no submission key, verifies no file bytes and starts no identity or screening provider call. The final submission is validated again.

2. Keep each credential in its own role.

CredentialPurposeBoundary
X-Homito-Customer-KeyA separate server key with write:applications for the customer's shared intake budget and hosted eID handoffServer only; same company as the advertised unit; does not replace applicant capabilities
X-Application-Submission-KeyOne UUID v4 generated before the first submissionKeep the same key and accepted request body/files after a lost response; it is not a landlord Idempotency-Key
X-Application-ReceiptJSON containing kind: "public", the original unit slug as target, and the original submission key as keySend in the receipt header, never a URL; it proves only commit existence and attachment state
X-Application-Upload-KeyA separate capability generated from 32 cryptographically random bytes encoded as 64 lowercase hexadecimal charactersRequired for manifest upload operations; a receipt, application ID or company key cannot replace it

The customer key changes the shared company traffic budget; it does not grant another company's access or replace receipt/upload permissions. An invalid supplied key fails closed instead of falling back to public access. Keep company keys on the server and all capabilities out of public caches, URLs, logs and agent conversations. Preserve them in the visitor's protected form session before the first network request.

3. Complete the required identity handoff, then submit once.

When identityGate.required is true, use the published hosted eID handoff. An administrator first registers the customer's exact HTTPS callback through the existing authenticated control plane. On the customer server, bind random state, a private PKCE verifier and its S256 challenge to the current visitor's protected form session, the current form/round and the original submission key. Create the handoff at POST /api/v1/public/units/{slug}/application-eid/handoffs using the same company's server key and the returned form contract.

Persist the handoff state before redirecting to the returned authorization URL and honor its expiresAt. At the registered callback, validate state against that same visitor's session; a global state lookup is insufficient. Keep the code/verifier private, suppress callback query logging and redirect immediately to a clean form URL. A key, account, hasNoPersonalNumber flag or client assertion cannot bypass required eID.

Submit POST /api/v1/applications as multipart/form-data with X-Homito-Application-Contract: 1 and the original X-Application-Submission-Key. Send an accurate Content-Length; a missing or mismatched length is rejected. Include unitSlug, listingPeriodId, formVersion and the JSON application fields. Where required, send the separate identityHandoff JSON field containing handoffId, code and codeVerifier; never put it inside application or expose the verifier to browser code. For a handoff-backed submission, also send the exact original customer key that created the handoff; another key for the same company cannot substitute. Legacy JSON intake does not provide this versioned contract.

Use the form's documents.maxExternalBodyBytes for the whole multipart request. Inline files use the published attachment fields; larger documents use attachmentManifest with no inline files and the separate upload key. Follow documents.manifestSchema, maxFiles, maxFileBytes, allowedMimeTypes and acceptedByteFormats, including the requirements for the main applicant and every co-applicant. A manifest declares intended files; it is not received evidence.

4. Read the receipt before deciding what to retry.

Read GET /api/v1/applications/receipt with the original receipt header. received means the application committed; inspect its separate attachments state rather than creating another submission. An identical committed retry returns the original receipt before rechecking a consumed or expired one-use identity proof. unknown can mean not sent, still processing or rolled back: use bounded backoff and, when retrying, the identical request with the original key. removed is terminal; stop old retries. Receipt reads grant no applicant profile, document download, screening result or decision rights. notifications: unknown is not proof of email delivery.

5. Upload declared bytes and finalize.

All four operations below require the original X-Application-Upload-Key. Preserve every manifest upload ID and the original file bytes/hash across interruption.

OperationWhat it confirms
GET /api/v1/applications/{applicationId}/uploadsCurrent finalized flag and each declared file's pending, received, expired or stale state
POST /api/v1/applications/{applicationId}/uploads/{uploadId}/urlA short-lived PUT URL and exact headers, or an already-received result that needs no PUT
POST /api/v1/applications/{applicationId}/uploads/{uploadId}/completeActual bytes, length, SHA-256, byte format/MIME and current subject binding were checked and the attachment committed
POST /api/v1/applications/{applicationId}/finalizeAll declared documents are committed in the ordinary document store; otherwise the operation is refused

Upload only to the exact returned URL with its exact method, headers and byte length. Never send the company key or applicant capabilities to object storage, follow redirects or log the signed URL. A successful PUT alone is not completion. A customer-server upload proxy avoids assuming that direct browser-to-storage CORS is configured.

After a lost PUT response, try complete for the same upload ID before uploading again. On upload_missing, obtain another URL for that same ID, upload the original bytes and complete it. Refreshing an expired PUT URL does not renew the underlying upload capability. Stop at expired capability or changed applicant/round scope; changing a file or person requires the explicitly authorized correction/completion flow, not a new submission key or a modified accepted manifest.

Recover by error code, without creating a second application.

FailureNext action
eid_required (403)Complete the approved hosted identity flow; do not invent a bypass
form_version_changed / application_round_changed (409)Reload the current contract and ask the applicant to confirm changed information before a new accepted submission; preserve any existing receipt
submission_payload_changed (409)Reconcile the original accepted body/files; do not automatically create another operation
upload_subject_changed (409), stale or expired upload accessStop upload/finalization and use the authorized completion path for the current subject
submission_removed (410)Stop retrying the removed submission
File/format validation failure (400/411/413/415/422)Correct the reported request or document issue before an authorized retry
429, 503 or a lost responseHonor Retry-After when supplied, retain the checkpoint and read receipt/progress before an original-key retry

application.submitted and application.intake_finalized are separate webhook events. Verify/deduplicate them as described in the webhook guide and use receipt/progress for current state. Neither an event nor finalization asserts email delivery, a rental decision or a completed screening. Keep applicant data and upload/receipt capabilities outside public listing caches.

Keep private data out of public caches

Keep personal data, receipts and upload capabilities out of public listing caches. Tenant and application reads intentionally return minimized operational projections; do not infer missing private fields or screening conclusions. Keep nullable/unknown values distinct from zero or a completed result.