Notifications
POST /Notifications is the self-service callback on this
API. It is not the agent webhook pipeline (topics such as
updateApplicationStatus). That system is documented under
Webhooks.
Registration stores one HTTPS URL for the authenticated BOX user (negotiator). BOX POSTs a small JSON body when a subscribed referencing event happens for an applicant that user owns. You can still poll reports if you do not use notifications.
Registration replaces any previous URL, key, and event list for that
user. It is not per application, and it is not the agent
WebhookAddress.
sequenceDiagram
autonumber
actor CRM
participant API as Lettings Hub
CRM->>API: POST /Notifications
API-->>CRM: 200
Note over API: referencing completes
API->>CRM: POST JSON plus x-webhook-signature
CRM->>API: GET report PDF
API-->>CRM: 200, PDF
Register once per BOX user, then fetch the PDF when a report event arrives.
Register
{
"Uri": "https://crm.example/hooks/lettings-hub",
"Key": "shared-secret",
"Notifications": [5, 11, 12, 13]
}
Example body. Uri is required. Empty Notifications returns 406.
| Code | Event |
|---|---|
| 5 | Form submitted |
| 11 | Reference processed (one income reference finished) |
| 12 | Interim report done |
| 13 | Final report done |
Inbound POST
BOX sends Content-Type: application/json. The body is:
{
"ReferenceNo": "s0013524",
"Notification": 13,
"Message": "Elite Report created"
}
| Field | Meaning |
|---|---|
ReferenceNo |
Applicant reference (tenant or guarantor), not the application reference. |
Notification |
Event code as a number (5, 11, 12, or 13). |
Message |
English description. Useful to log; do not parse it. |
ReferenceNo alone. Store
applicant reference → application reference when you create the tenant.
See Identifiers.
Signature
If you sent Key at registration, BOX adds header
x-webhook-signature: hex-encoded HMAC-SHA256 of the
raw JSON body, keyed with Key. Both key
and body are hashed as ASCII bytes. Compare in constant time. If you
omitted Key, there is no signature header.
import hmac, hashlib
def valid(body: bytes, key: str, header: str) -> bool:
expected = hmac.new(key.encode("ascii"), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
Verify against the raw request body, not a re-serialised object.
After a report event
12 and 13 mean a PDF is available. Fetch it with
GET /Applications/{ApplicationRef}/{ApplicantRef}/Reports/{ReportType}.
The notification does not include the file.
If your endpoint fails, BOX logs the error and does not retry. Make the handler idempotent; event 11 in particular can fire more than once (once per income reference).