AlphaAlpha Docs

Partner API

The API an integration partner such as VolgJeWoning uses to map projects and bouwnummers, read a buyer's confirmed choices, and hand a signed-in buyer over to their own portal

Partner API

An integration partner sits in front of the buyer: the buyer signs in there, sees their home there, and clicks through to Alpha to configure it. This API is what that partner drives Alpha through.

It was built for VolgJeWoning, and the shape follows what a partner actually has to do, in order:

  1. match its own project against an Alpha project;
  2. match its own woningen against Alpha bouwnummers, and store that mapping;
  3. send a buyer into their house without a second login;
  4. read back the confirmed choices to process in its own system.

The endpoints are deliberately hand-written and narrow rather than the generic CRUD surface. A partner contract has to stay stable across our own refactors, and the internal entities carry plenty a partner has no business seeing: cost prices, worker events, model instances.

Authentication

Every call takes an API key scoped to one tenant and carrying the READ_PROJECTS permission. Create one under Settings → API keys; see Roles & permissions for how the permission is granted.

A partner key reads exactly what a read-only staff account of that tenant could, and nothing more. There is no cross-tenant partner key: issue one per tenant.

Everything below is relative to the API base path, and every path starts with the partner's own namespace code:

/partner/{partnerCode}

partnerCode (vjw, 4ps, …) is a namespace, not a credential. The API key decides what the caller may do; the code only decides whose mapping is read and written, so two partners never overwrite each other's references on the same house.

Errors

StatusWhen
401Missing or invalid API key
403The key lacks READ_PROJECTS, or the tenant is inactive
404The project, house or configuration does not exist in the key's tenant
400The request cannot be honoured, with a message saying why

A record in another tenant answers 404, never 403. The response must not confirm that something exists somewhere else.

Mapping projects

GET /partner/{partnerCode}/projects

Every project in the tenant. Match on slug, which is the closest thing Alpha has to a project number, and fall back to name.

[
  {
    "id": "project-1",
    "name": "De Waarden fase IV",
    "slug": "de-waarden-iv",
    "phase": "SALES",
    "houseCount": 48
  }
]

houseCount counts houses across every scenario, as a sanity check that both sides matched the same plan.

Mapping houses

GET /partner/{partnerCode}/projects/{projectId}/houses
[
  {
    "bnr": "12",
    "projectId": "project-1",
    "blockId": "block-1",
    "typeId": "type-a",
    "status": "SOLD",
    "hasConfirmedConfiguration": true,
    "confirmedConfigurationId": "config-a",
    "configurationCount": 3,
    "hasBuyer": true,
    "externalRef": "VJW-9912"
  }
]

bnr is the bouwnummer, and it is the natural key both sides recognise. It is unique within the project, not across the tenant, and partners spell it differently (12, BNR 12, 0012), which is why a confirmed mapping is stored rather than re-derived on every call:

PUT /partner/{partnerCode}/projects/{projectId}/houses/{bnr}/external-ref
{ "externalRef": "VJW-9912" }

Send a blank or null externalRef to clear it. The mapping is stored under the calling partnerCode only, so writing a VolgJeWoning reference never disturbs the 4PS one.

hasBuyer says whether a buyer is recorded, without exposing who: needed to know whether a sign-in hand-off can work at all, and nothing more.

Handing a buyer over

POST /partner/{partnerCode}/projects/{projectId}/houses/{bnr}/sign-in-url
{ "redirectPath": "/portal/configurations" }
{
  "url": "https://alpha.asrr.nl/portal/magic-link?token=…&tenant=…&redirect=…",
  "bnr": "12",
  "expiresAt": "2026-09-11T09:42:00Z"
}

Hand the returned URL straight to the buyer's browser. The buyer lands signed in, on their own house by default, or wherever redirectPath points inside the portal.

Request one per click. Never store one. The URL is single-use and expires in minutes. This is deliberately unlike the link in a configuration confirmation email, which stays valid and reusable for 30 days because it lives in a mailbox the buyer returns to. A partner key that could mint 30-day reusable links for any bouwnummer would be a master key to every buyer's portal.

Two things are refused rather than honoured:

  • An email that is not the buyer recorded on the house. The field exists for partners that are authoritative about who the buyer is; supplying a different address is refused, so the endpoint cannot mint a portal session for an arbitrary person on someone else's bouwnummer. Omit it and the recorded buyer is used.
  • A redirectPath that is not a path on our own frontend. Anything not starting with / is ignored and the buyer lands on their house.

400 also comes back when no buyer is recorded on the house, when the buyer has no email address, when the buyer portal is switched off for the tenant or project (see Configurations & drawings), or when the address belongs to an account Alpha must not auto-provision, meaning staff or an unverified signup.

Reading the choices

A house can carry several configurations, because a buyer may iterate until their phase closes. The partner must be told which one is definitive rather than assuming the newest is.

GET /partner/{partnerCode}/projects/{projectId}/houses/{bnr}/configurations

Newest first. confirmed marks the one assigned to the house; source is CUSTOMER when the buyer submitted it themselves and INTERNAL when staff did.

Then fetch the kassabon:

GET /partner/{partnerCode}/configurations/{configurationId}
{
  "id": "config-a",
  "projectId": "project-1",
  "projectName": "De Waarden fase IV",
  "bnr": "12",
  "typeId": "type-a",
  "typeName": "Type A hoek",
  "status": "COMPLETED",
  "source": "CUSTOMER",
  "confirmed": true,
  "favorite": true,
  "basePrice": 385000.0,
  "totalPrice": 12450.0,
  "options": [
    {
      "code": "03.12.040",
      "option": "Keukenopstelling",
      "category": "Keuken",
      "description": "Uitbouw 1,2 m",
      "price": 9800.0,
      "base": false
    }
  ]
}

code is the option code as Alpha knows it, which is the code imported from 4PS where that integration is configured (see 4PS integration). That is the value to map onto the partner's own product list.

base: true marks the standard choice rather than an upgrade; partners normally write back only the upgrades. basePrice is the house's v.o.n. price where the project carries one, and totalPrice is the sum of the option lines, excluding it.

The totals come from the same enrichment behind the buyer's own portal page and the PDF receipt, so a partner and a buyer can never see different numbers for one configuration.

Buyers who are not on the partner platform

Buyers of an external builder have no account with the partner, so the hand-off above does not apply to them. They reach the portal through the invite from the sales module instead, which is the route that depends on no other system. See Getting buyers into the portal.

On this page