WzGate
Build your own website

Changelog

Every change to the Wzgate public API — what was added, what is deprecated and when it goes, and what has been removed.

The public API is a contract. A breaking change is announced here first, the old behaviour keeps working for a stated period, and only then is it removed. Nothing is withdrawn without appearing on this page with a date.

Watch this page — it is the only place a removal is announced.

2026-09-19

Changed — owner listings

  • POST /public/owner-listings needs a signed-in, verified visitor. Send the visitor's token as Authorization: Bearer <token>. An anonymous call is a 401; only a verified account can sign in, so a token is proof enough. The listing is filed under that account — the API no longer matches owners by a typed phone or email.
  • name and phone are optional; the account's own name labels the owner when they are missing. email is accepted and ignored — the account's address is already known. It will be removed from the body with notice here.
  • mediaUrls is removed. A body that still sends it is a 400. Photos are uploaded to the listing itself, so the flow is now three calls:
    1. POST /public/owner-listings with draft: true — creates a draft.
    2. POST /public/owner-listings/:id/media — upload photos (multipart, up to 20, 8 MB each, JPEG/PNG/WebP).
    3. POST /public/owner-listings/:id/submit — sends it for review.
  • Submit needs at least five photos. Submit, resubmit and a non-draft create answer 400 PHOTOS_TOO_FEW with details: { min, count } below that. Drafts and PATCH are never blocked by it.
  • PATCH /public/owner-listings/:id accepts name and phone, and GET /public/owner-listings/:id returns them, so an edit form can prefill what was saved rather than the profile.
  • mine and :id return listing: { slug, status } once staff approve the request and a property exists. slug is present only while the listing is public — link to it when it is there, and not otherwise.
  • Owner-listing routes require the realestate.secondary module. An organization without it answers 403 MODULE_NOT_ENABLED on every /public/owner-listings/* route. Read features.ownerListings (below) and hide your "sell" entry points when it is false.

Changed — visitor sessions

  • Tokens can be revoked. Logout, a password change, a password reset, account deletion and staff disabling the account all end every session the account holds. A revoked token is a 401 VISITOR_SESSION_EXPIRED: clear it and send the visitor to sign in. A disabled account is 403 VISITOR_DISABLED on sign-in and on any signed-in call.
  • POST /public/me/password returns a fresh token. The change revokes the token that made the call, so store the new one or the next request is a 401 VISITOR_SESSION_EXPIRED. A wrong current password is a 401 that does not end the session.
  • Logout reads the Bearer token from Authorization, like every other signed-in call — no body needed.
  • resend-otp is bot-checked and rate-limited per account. Send X-Turnstile-Token as on the other protected writes; a resend inside the cooldown is 429 RESEND_TOO_SOON with details.retryAfterSeconds — show a countdown instead of retrying.
  • An unverified sign-in returns details.visitorId with 401 VISITOR_EMAIL_NOT_VERIFIED, so you can go straight to the OTP screen and call resend-otp without asking the visitor to register again.
  • Reset links carry <visitorId>.<secret> as the token. Pass it through unchanged. A malformed, old-format, used or expired token is 400 RESET_TOKEN_INVALID.

Added

  • Register takes intent and acceptTerms. intent is optional, BUYER or SELLER — a hint recorded for the CRM that grants nothing. acceptTerms is optional but must be true when sent; send it when your form has a terms checkbox.
  • GET /public/company-profile gains features.ownerListings (boolean — whether owner listings are open on this site) and site.ownerReviewSlaHours (number or null — how long staff aim to take over a review, for "we reply within …" copy).
  • A registered domain alone identifies the site on every public route. A browser call from one of the site's domains needs no header: the Origin is enough, including on module-gated routes, which previously still demanded X-Subdomain. X-Site and X-Subdomain keep working. See Identify your site.

Error codes

CodeStatusMeaningالمعنى
VISITOR_TOKEN_REQUIRED401A signed-in route (now including POST /public/owner-listings) was called with no token.استُدعي مسار يتطلّب تسجيل الدخول بلا رمز.
VISITOR_SESSION_EXPIRED401The token was revoked by logout, a password change or reset, or account deletion.أُلغي الرمز بتسجيل الخروج أو تغيير كلمة المرور أو استعادتها أو حذف الحساب.
VISITOR_DISABLED403Staff disabled the account.عطّل فريق العمل الحساب.
VISITOR_EMAIL_NOT_VERIFIED401Sign-in before the email is verified; details.visitorId is set.تسجيل دخول قبل تأكيد البريد؛ ويأتي details.visitorId.
RESEND_TOO_SOON429resend-otp inside the cooldown; wait details.retryAfterSeconds.إعادة إرسال الرمز خلال مهلة الانتظار؛ انتظر details.retryAfterSeconds.
RESET_TOKEN_INVALID400The reset token is malformed, old-format, used or expired.رمز الاستعادة مشوّه أو بالصيغة القديمة أو مستخدم أو منتهٍ.
PHOTOS_TOO_FEW400Submitting a listing with fewer than five photos; details: { min, count }.إرسال عرض بأقل من خمس صور؛ ويأتي details: { min, count }.
MODULE_NOT_ENABLED403The organization does not have realestate.secondary, so owner listings are closed.المؤسّسة لا تملك realestate.secondary، فعروض المُلّاك مغلقة.

2026-09-14

Added

  • X-Site — identify a site by its key instead of by its organization. An organization can now run several public websites (real estate, stays, company profile) and a request resolves a site, not just a tenant. A verified domain resolves the site with no header at all. See Identify your site.
  • X-Api-Key — per-site publishable (pk_…) and secret (sk_…) keys, created in the CRM under Settings → Websites → Connect your website. A publishable key is only valid from the site's own domains; a secret key is for your server and gets five times the rate limits. Currently in monitor mode: a call without a key is served and the refusal is logged. Enforcement will be announced here before it is switched on. See Keys and CORS.
  • X-Turnstile-Token — a Cloudflare Turnstile token on the five writes bots abuse: leads, viewing requests, visitor register, forgot-password and newsletter. Required with a publishable key, skipped for secret-key calls, inactive where the site has no Turnstile configured. See Bot protection.
  • GET /public-openapi.json — a machine-readable OpenAPI 3 document containing only /api/public/*, with the headers above declared on every operation. It is the source of the API reference in these docs, and the thing to point a client generator at.
  • Legal text on the company profile — legal.privacy and legal.terms arrive as { en, ar } rich text beside the existing privacyUrl / termsUrl. A site can now render its own /privacy-policy and /terms from the CRM instead of hard-coding copy: if a URL is set, redirect to it; else if text is set, render it; else fall back to your own page.
  • Viewing validation errors — VIEWING_SLOT_INVALID, VIEWING_DAY_CLOSED, VIEWING_SLOT_TOO_SOON (400) and VIEWING_SLOT_TAKEN (409). Booking now respects the site's working days, slot list, time zone and minimum notice, and a slot can be held by only one live request. Previously a booking outside the rules was accepted and two visitors could take the same slot. See Errors.

Deprecated

  • X-Subdomain — still honoured, and still resolves an organization's default site, so existing websites keep working unchanged. It cannot name one site among several, which is why X-Site replaces it. Removal date: to be announced here, with notice, once live sites have moved. Migration is one line: send X-Site: <site key> (from the Connect tab) instead of, or alongside, X-Subdomain.

Removed

  • /api/public/promotion/* — the owner "promote your listing" flow and its plans are gone from the API and from the CRM. Nothing replaces it. A website still calling those paths gets a 404; remove the calls and any UI that offered promotion.

On this page