Webhooks

Agent webhooks are outbound POSTs from BOX to URLs stored on the agent (WebhookAddress). They carry a topic (or a KYC / report payload without one) and are retried if delivery fails. They are not registered through this API.

Subscribe

Give Lettings Hub an HTTPS URL that accepts POST with Content-Type: application/json. Optionally a static header name and value (for example X-API-Key); BOX sends it on every delivery to every URL. BOX does not sign deliveries to partner URLs, so use that header and HTTPS to authenticate them. Every request carries User-Agent: Homespun-Webhook-Client/1.0.

Several URLs can be stored comma-separated. Each attempt POSTs the same body to every URL. Delivery counts as successful if any URL returns 2xx; the others are still called.

Ask your account manager to enable WebhookEnabled and set the address.

What to return

Return any 2xx as soon as you have accepted the body. BOX does not read the response body. Process heavy work asynchronously. Ignore unknown fields and unknown topics.

A 4xx other than 408 or 429 tells BOX the delivery is rejected for good: it stops retrying that payload and marks it dead. Anything else (5xx, 408, 429, a timeout, a dropped connection) is retried as below. So return 4xx only when you never want that payload again.

Delivery

The first send is attempted when the event is raised, with a 10-second timeout. If that fails, the payload is stored and a background sender retries it about once a minute, fifteen attempts in all; each of those attempts itself retries up to three times on 408, 429, 502, 503, 504 or a dropped connection. After the fifteenth failure the payload is marked dead and logged. A permanent 4xx (above) marks it dead at once.

Duplicates can occur. Deduplicate on topic plus applicationRef and any stable ids inside data. For KYC use KYCid; for reports use URL or EntryType plus applicant ids.

Envelope

Most events include topic, applicationRef (the tenancy / application reference), optional data, and metadata: EntryType, CreationDate, ApplicationId, ApplicantId, ApplicationExternalReference, ApplicantExternalReference.

KYC status has no topic — look for KYCid and EntryType Status Update KYC. Report available sets EntryType Report Available plus ReportType and URL; because that payload is built on the applicant-status type, you will usually also see topic updateApplicant. Key off EntryType or URL, not topic alone.

Topics

Every topic BOX sends today. Ignore any topic you do not handle.

Topic When
createNewTenancyA tenancy is created
addApplicantAn applicant is added
updateApplicantAn applicant’s status changes (also carried by Report available)
applicantReopenedA completed applicant is reopened for more referencing work
updateApplicationStatusThe tenancy step changes
uploadReapitDocumentA document is ready (base64 fileData or a presignedUrl)
prescribedInformationCompletedPrescribed information (deposit protection) is submitted
tenancyFinalisedThe tenancy agreement is fully signed and the tenancy finalised
tenancyStartDateChangedStart date changes (yyyy-MM-dd)
addNoteA note or audit-trail line is written against an applicant or the tenancy
rentIncreaseNoticeIssuedThe agent issues a rent-increase notice
rentIncreaseNoticeWithdrawnAn issued notice is withdrawn
rentIncreaseWithdrawalFinalisedThe withdrawal-notice document is finalised
rentIncreaseAppliedThe increase takes effect on its start date
rentIncreaseTribunalReferralA tenant refers the increase to the tribunal
rentIncreaseTribunalReferralWithdrawnThe referral is withdrawn
rentIncreaseTribunalDecisionConfirmedThe agent records the tribunal’s decision
agreedRentConfirmationIssuedThe agent issues an agreed-rent confirmation after a rent review
agreedRentConfirmationFinalisedThat confirmation document is finalised

updateApplicationStatus

{
  "topic": "updateApplicationStatus",
  "applicationRef": "0013524",
  "applicationStatus": 4,
  "isPrescribedInformationRequired": true,
  "isPrescribedInformationCompleted": false,
  "isTenancyFinalised": false,
  "howWillSignApplicants": 1,
  "howWillSignAgentLandlord": 1,
  "EntryType": "Update Application Status"
}

applicationStatus is the tenancy step:

ValueStep
1Referencing in progress
2Referencing complete
3Tenants confirming preferences
4Awaiting tenancy agreement
5Awaiting tenant signing
6Awaiting landlord signing
7Awaiting agent signing
8Awaiting agent to confirm inventory
9Awaiting move-in meter readings
10Awaiting move-out meter readings
11Complete
12Tenancy archived

updateApplicant

data.applicantRef is the applicant reference. applicationStatus here is not the table above: it is 0 while the application is still starting, otherwise 1. When the applicant is completed, a full Applicant object may be included.

{
  "topic": "updateApplicant",
  "applicationRef": "0013524",
  "applicationStatus": 1,
  "Status": "References in Progress",
  "data": {
    "applicantRef": "s0013524",
    "applicantStatus": "References in Progress",
    "reportType": 3,
    "rentShare": 50.0
  },
  "EntryType": "Status Update"
}

addApplicant

Same data shape as above, plus type (tenant / guarantor) and reapitContactId (the applicant’s ExternalReference). The top level also carries Status, and the full Applicant object when the applicant is already completed. applicationStatus on this topic is the tenancy step integer.

applicantReopened

{
  "topic": "applicantReopened",
  "applicationRef": "0013524",
  "applicantRef": "s0013524",
  "EntryType": "Applicant Reopened"
}

Note applicantRef sits at the top level here, not inside data. The applicant goes back to in progress; expect further updateApplicant events.

createNewTenancy

data.address is an object (addressLine1, addressLine2, town, county, postcode, country), not a single formatted string. data.applicants lists the people on the tenancy (reapitContactId, applicantRef, type, applicantStatus, reportType, rentShare). data also carries reapitTenancyId and reapitApplicantId (your external references), reapitCustomerEmail, branchId, branchName, agentId, referencingServiceType and applicationType (integers). Sent only for tenancies created through the Reapit integration.

Document available

{
  "topic": "uploadReapitDocument",
  "applicationRef": "0013524",
  "data": {
    "documentType": 2,
    "fileName": "Tenancy_Agreement.pdf",
    "presignedUrl": "https://…s3…/Tenancy_Agreement.pdf?X-Amz-Expires=86400&…"
  },
  "EntryType": "Document available"
}

data comes in one of two shapes. The usual one carries a presignedUrl: fetch the file from it within 24 hours, no Token needed. If publishing the file failed, the same payload carries base64 fileData instead — cap your JSON parser, the file can be large. Handle both. For a per-applicant document (signed receipt, KYC reports) the envelope also has ApplicantId and ApplicantExternalReference.

documentType is an integer:

ValueDocument
0Signed receipt of documents
1Landlord confirmation document
2Tenancy agreement
3Tenancy agreement audit trail
4Tenancy audit trail
5Proof of inventory
6Additional attachments
7Tenant guide
8How to Rent guide
9How to Rent guide (easy read)
10Tenant leaflet
11Deposit scheme leaflet
12Deposit scheme — custodial
13Deposit scheme — insured
14Inventory document
15Counter indemnity
16Gas safety certificate
17Energy performance certificate
18Electrical certificates
19Prescribed information
20Deposit certificate
21Deposit terms and conditions
22Other
23Identity check report
24Right to Rent report
25Anti-money-laundering report
26PEPs and sanctions report
27Professional guarantor agreement
28Rent increase notice
29Agreed rent confirmation
30Withdrawal notice

KYC (no topic)

{
  "Status": "COMPLETED",
  "KYCid": "445566",
  "UserType": 1,
  "Email": "applicant@example.com",
  "Reports": {
    "100": "https://pre-prod.lettingshub.co.uk/webapi/v2/KYC/Report/445566/100",
    "101": "https://pre-prod.lettingshub.co.uk/webapi/v2/KYC/Report/445566/101"
  },
  "EntryType": "Status Update KYC"
}

Status is a string: SENT, IN PROGRESS, CONFIRMATION, COMPLETED, CANCELLED, EXPIRED or ABANDONED. UserType is the KYC user type (1 tenant, 2 guarantor, 3 permitted occupier, 4 landlord, 5 buyer, 6 seller, 7 other). Reports is only present when the check is complete; its keys are report types — 100 identity, 101 Right to Rent, 102 anti-money-laundering, 103 PEPs and sanctions — one per check that was requested, and the same integer ends each URL. Fetch a report with your Token header. There is no applicationRef on this payload.

Report available

{
  "ReportType": 2,
  "applicationRef": "0013524",
  "URL": "https://pre-prod.lettingshub.co.uk/webapi/v2/Applications/0013524/s0013524/Reports/2",
  "EntryType": "Report Available"
}

Fetch the PDF with that URL (and your Token header). The same POST also includes topic updateApplicant, Status, applicationStatus and data, because it is built on the applicant-status payload. ReportType uses the report-type integers from Dictionaries; a final report usually arrives as a pair, the report itself and 7 (Summary). A legacy Property Passport applicant sends Report Available with no topic and a different URL; ignore those.

addNote

{
  "topic": "addNote",
  "applicationRef": "0013524",
  "Timestamp": "2026-09-15T10:12:44",
  "Author": "Jane Agent",
  "Content": "Called the employer, awaiting reference.",
  "data": {
    "typeId": "MI",
    "description": "Called the employer, awaiting reference."
  },
  "EntryType": "UserNote"
}

EntryType is UserNote, SystemNote or AuditTrail for applicant and reference notes, which also carry Timestamp, Author and Content at the top level. A professional-guarantor note comes on the same topic with EntryType Professional Guarantor Update and only data. data.typeId is always MI.

Other tenancy topics

prescribedInformationCompleted includes isTenancyFinalised, schemeProvider and schemeType (the deposit scheme, as text). tenancyFinalised includes hasInventory, daysToReportIssues (may be null), and the prescribed-information flags. tenancyStartDateChanged sends oldStartDate (null when there was none) and newStartDate.

Rent increase and agreed rent

These topics follow a rent review on an existing tenancy. All of them carry applicationRef, reapitTenancyId (the tenancy’s ExternalReference) and ApplicationId at the top level, plus the fields below. Money is a number, dates are yyyy-MM-dd, frequency is one of weekly, fortnightly, 4-weekly, monthly. Where a document is named, fetch it through the matching uploadReapitDocument event (types 28–30); the documentS3Key is an internal key, not a URL.

{
  "topic": "rentIncreaseNoticeIssued",
  "applicationRef": "0013524",
  "reapitTenancyId": "RPT-TEN-000123",
  "ApplicationId": 13524,
  "noticeId": 871,
  "documentS3Key": "rent-increase/0013524/871/notice.pdf",
  "documentName": "Rent Increase Notice.pdf",
  "journalNoteText": "Rent Increase Notice issued to the tenants.",
  "statusText": "Rent Increase Notice issued. New rent of £1,300 monthly due to start from 01 Jan 2027.",
  "newRentAmount": 1300.0,
  "newRentFrequency": "monthly",
  "newRentStartDate": "2027-01-01",
  "EntryType": "Rent Increase Notice Issued"
}
TopicFields beyond the common three
rentIncreaseNoticeIssuednoticeId, documentS3Key, documentName, journalNoteText, statusText, newRentAmount, newRentFrequency, newRentStartDate
rentIncreaseNoticeWithdrawndocumentS3Key, documentName, journalNoteText, statusText (Complete)
rentIncreaseWithdrawalFinaliseddocumentS3Key, documentName
rentIncreaseAppliedjournalNoteText, statusText (Complete), appliedRentAmount, appliedRentFrequency, appliedFromDate, previousRentAmount
rentIncreaseTribunalReferraldateReferred, journalNoteText
rentIncreaseTribunalReferralWithdrawnjournalNoteText
rentIncreaseTribunalDecisionConfirmedconfirmedRentAmount, confirmedRentFrequency, confirmedRentStartDate, journalNoteText; documentS3Key and documentName only when a decision document exists
agreedRentConfirmationIssueddocumentS3Key, documentName, journalNoteText, statusText, agreedRentAmount, agreedRentStartDate
agreedRentConfirmationFinaliseddocumentS3Key, documentName

Example receiver

curl -X POST "https://crm.example/webhooks/box" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: <shared-secret>" \
  -d '{"topic":"updateApplicationStatus","applicationRef":"0013524","applicationStatus":4}'

Dispatch on topic when present, else on KYCid / EntryType. Return 204. Enums in payloads are integers (documentType, reportType, UserType), never names.

Also see: Notifications (POST /Notifications).

Deprecated. Use Webhooks instead.