Forms via the Headless Interface
A custom front end displays its forms itself. What a visitor enters there is received by WP-ImmoMakler through the headless interface and handled exactly like an entry in the contact form of your WordPress website: the same mandatory fields, the same spam protection, the same route to the contact person, into your CRM software and into the list of enquiries in the back end.
What the website specifies
Section titled “What the website specifies”Under forms, GET /site describes how your forms are built, so that a front end reproduces them instead of inventing them:
forms.enquiry.fields: all fields of the contact form with their labels and the details whether the field is displayed (enabled) and whether it must be filled in (mandatory), as you defined it in the back end of your website (wp-admin) under WP-ImmoMakler → Settings → Contact Form.forms.enquiry.texts: the texts of the consents (terms and conditions, privacy, right of withdrawal, commission notice, custom checkbox) and of the selection fields “Viewing requested” and “I already own property” as HTML.forms.enquiry.withdrawal_always: whether the right-of-withdrawal notice must be confirmed for every property or only for properties with a commission.forms.withdrawal: the fields and consents of the withdrawal form, the introduction and confirmation texts and the address of the privacy policy.forms.spam: how spam protection is operated, see below.
Spam protection
Section titled “Spam protection”Every form on your website carries a signed token, an invisible field and, if you have set one up, a captcha. A front end fetches these details when it displays a form:
GET /wp-json/immomakler-headless/v1/spam/tokenThe response gives the token under token, the name of the invisible field under honeypot_field, the minimum number of seconds that must pass between display and submission under min_fill_seconds, and under captcha the provider, the field name and the public key of the captcha (for ALTCHA, under challenge_url the address from which the widget fetches its challenge, under algorithm the algorithm of the challenge and, if that is ARGON2ID, under worker_url the Argon2id worker; for Friendly Captcha in the EU region, under endpoint the value for the widget’s data-api-endpoint or data-puzzle-endpoint). On submission the token is sent back under spam.token, the invisible field empty under spam.honeypot, and the captcha solution under spam.captcha.
If your website uses ALTCHA, WP-ImmoMakler issues challenges using the Argon2id algorithm on every server with the PHP Sodium extension. The ALTCHA package on its own only solves PBKDF2 and SHA; your front end must register the Argon2id worker with the widget before it solves a challenge, otherwise every challenge fails with “Unsupported algorithm”. With the npm package altcha and a bundler such as Vite:
import 'altcha';import Argon2idWorker from 'altcha/workers/argon2id?worker';
globalThis.$altcha.algorithms.set('ARGON2ID', () => new Argon2idWorker());Without a bundler, register the worker from worker_url. A worker must come from the same address as the page, so load it through a blob that fetches the script: new Worker(URL.createObjectURL(new Blob(['importScripts(' + JSON.stringify(workerUrl) + ')']))). The widget itself receives challenge_url in its challenge attribute; the route answers requests from any address.
Whether a suspicious sender is rejected or the enquiry is only delivered with a flag is determined, as on the website, by the setting under WP-ImmoMakler → Settings → Spam protection. The limit on requests per hour and visitor applies as well.
If it is not the visitor’s browser but the server of your front end that sends the forms to the interface, all visitors look like a single one to WP-ImmoMakler. In that case, enter the IP addresses of these servers as trusted proxy addresses in the back end of your website (wp-admin) under WP-ImmoMakler → Integrations → Headless; WP-ImmoMakler then reads the visitor’s address from the X-Forwarded-For header that your server sends along.
If your front end runs with a provider such as Cloudflare, Vercel or Netlify, the provider’s servers have no fixed address. In that case, enter the address ranges the provider publishes, in CIDR notation, one per line, for example 173.245.48.0/20 or 2400:cb00::/32; for Cloudflare they are listed at cloudflare.com/ips. A visitor cannot forge the header in their favour: WP-ImmoMakler takes the last address in the header that does not itself belong to a trusted range, and that is the one the provider entered itself.
Who may send
Section titled “Who may send”The form routes accept requests only from the allowed origins, that is, from a browser on the address of your front end or one of the addresses entered under Allowed origins, or with the API key in the X-ImmoMakler-Key header when the server of your front end sends. Anything else is answered with 403 and the error code headless_forbidden.
Sending a contact enquiry
Section titled “Sending a contact enquiry”POST /wp-json/immomakler-headless/v1/enquiriesContent-Type: application/json
{ "properties": ["WP1235"], "fields": { "salutation": "Frau", "first_name": "Erika", "last_name": "Muster", "email": "erika@example.com", "phone": "+49 40 123456", "message": "Ich interessiere mich für die Wohnung.", "appointment_requested": true }, "consents": { "privacy": true, "withdrawal": true }, "spam": { "token": "…", "honeypot": "" }, "origin": "https://www.ihre-domain.de/immobilien/wohnung-in-hamburg/"}properties gives the object numbers of the properties enquired about; without properties it is a general contact request to the address set in the settings. fields contains the entries with the English field names from forms.enquiry.fields (key; source_key gives the name of the field in the WordPress settings), consents the consents (terms, privacy, withdrawal, commission, custom), and origin the page the enquiry came from; it is stated in the email.
On success the interface responds with 201 and gives the number of the saved entry under WP-ImmoMakler → Contact Requests (enquiry_id) as well as whether the enquiry was delivered flagged as suspicious (spam_suspected). Links to the properties in the email point to your front end.
If mandatory fields are missing or an email address is invalid, it responds with 422 and the error code headless_validation_failed; data.errors lists the affected fields (email, privacy, …). An unknown object number is reported as headless_unknown_property, an enquiry rejected as spam as headless_spam_rejected, and too many requests in one hour as 429 with headless_rate_limited. If an email could not be delivered, it responds with 502 and headless_delivery_failed; the enquiry is then still saved under WP-ImmoMakler → Contact Requests.
Sending a withdrawal
Section titled “Sending a withdrawal”POST /wp-json/immomakler-headless/v1/withdrawalContent-Type: application/json
{ "fields": { "first_name": "Erika", "last_name": "Muster", "email": "erika@example.com", "contract_identification": "Maklervertrag vom 1. Oktober 2026" }, "consents": { "withdrawal": true, "privacy": true }, "property": "WP1235", "source_url": "https://www.ihre-domain.de/widerruf/", "spam": { "token": "…", "honeypot": "" }}All fields and both consents are mandatory. WP-ImmoMakler sends the withdrawal to the configured address, files it in the archive and sends the visitor an acknowledgement of receipt, unless the submission is suspected to be spam. The 201 response gives the time of receipt under submitted_at. Missing details are reported as 422 with headless_validation_failed, and data.errors lists the fields (contract_identification, withdrawal, privacy, …).
Creating a property alert
Section titled “Creating a property alert”A property alert saves a search for an email address; WP-ImmoMakler then emails the subscriber new matches once they have activated the alert through the link in the confirmation email (double opt-in). Whether your website offers property alerts is stated by /site under forms.search_agent.active; it also holds the texts of the two consents (texts.contact, texts.privacy), the teaser and form text, whether the plain-text email option is offered (plaintext_option) and, under page_url, the page of your front end that the links in the emails point to.
POST /wp-json/immomakler-headless/v1/search-agentsContent-Type: application/json
{ "email": "erika@example.com", "criteria": { "marketing": "kauf", "type": "wohnung", "location": "hamburg", "price_max": 500000, "rooms_min": 3 }, "consents": { "contact": true, "privacy": true }, "notification": { "plaintext": false }, "spam": { "token": "…", "honeypot": "" }}criteria takes the same parameters as /search (the names of your website’s search form count as aliases); an empty object saves “all properties”. Both consents are mandatory. The response (201) gives the value pending under status and, under message, the note you show to the visitor; 422 reports missing consents, an invalid address or implausible criteria, and 429 too many property alerts in one hour.
The confirmation email leads to page_url with ?callback=confirm_searchagent&confirm=<hash>, and the unsubscribe link in every notification to page_url with ?callback=delete_searchagent&delete=<hash>. The page of your front end reads the property alert with GET /search-agents/<hash> (status, address, criteria as label/value pairs, link to the results list) and acts only on a click by the visitor: POST /search-agents/<hash>/confirm activates the alert, DELETE /search-agents/<hash> deletes it. Never act as soon as the link is opened: security scanners of email services call every link in an email. You set the path of the page in the back end of your website (wp-admin) under WP-ImmoMakler → Integrations → Headless as Path of the property alert page; without a front end entered, the links lead to the WordPress property list as before.