Certaby API · for property software

Certaby’s checks, inside your own software

Your software sends the people and the property. Certaby screens them against the sanctions lists, checks ID and Right to Rent by link, finds who owns a company, adds the property risk pack, and signs one certificate for the job.

Get your free test keyRead the reference

Already use Certaby? Sign in and open Developers.

Checks from 9p
Paid per check from your Certaby credit. No contract and no monthly fee.
A free test key
50 real checks, with nothing charged.
One base address
api.certaby.com
One certificate a job
Signed, with a public page anyone can check it on.

Who it’s for

Agency CRMs and letting software

Run the tenant, landlord, buyer and seller checks from the record your users already have open, and keep the certificate with the let or sale.

Proptech and portals

Check the people behind an application or a listing before it goes further, with the property risk pack on the same job.

Property lenders and lending platforms

Onboard borrowers and the companies behind them: sanctions and PEP, the owners through every layer of companies, and ID by link. Each lender’s name can go on its own certificates.

What it covers

  • Sanctions and PEP screeningEvery person and company checked against the UK, UN, EU and US sanctions lists, updated every morning. Add PEP and adverse media where the job needs it.
  • ID and Right to Rent by linkThe person checks their ID on their own phone. For a tenant in England, the same link checks their right to rent too. The result and the report come back to the job.
  • Company and owners (KYB)The Companies House record, the directors and the owners through every layer of companies, each one screened. The KYB package adds the FCA register, a business risk score and PEP on the people.
  • Property risk packThe EPC and MEES verdict, flood risk and crime for the postcode, on the same job and the same certificate.
  • Monitoring until completionEveryone in the job is screened again every morning until you mark it complete. The first 6 weeks are included.
  • A signed certificate for each jobHash-bound, with a public page on certaby.com where anyone can check it. Your team sees the same job under Transactions on certaby.com.
  • WebhooksSigned events when a run finds something to review, a certificate is re-issued, or the daily watch raises an alert.

Prices

Paid per check from your Certaby credit, the same balance your team uses on the website. The lower price applies from 1,000 checks of a kind a month on your account.

  • Sanctions screen, a person or company9p

    4p from 1,000 a month

    Against the UK, UN, EU and US lists, updated every morning.

  • PEP and adverse media, a person30p

    20p from 1,000 a month

    Added to the sanctions screen: whether the person is politically exposed (a PEP), and serious adverse news.

  • Company and owners, a company£1.79

    99p from 1,000 a month

    The Companies House record, the directors and the owners through every layer of companies, each screened and checked against the register of disqualified directors (up to 30 people).

  • KYB package, a company£3.99

    The company and owners check, the company's own screen, the FCA register and Warning List, a business risk score, and PEP and adverse media on up to 4 of its people.

  • Property risk pack, a property79p

    49p from 1,000 a month

    The EPC and MEES verdict, flood risk and crime for the property's postcode.

  • ID check by link, a person£1.99

    Their ID document and a live selfie, checked on their own phone. The report comes back to the job.

  • ID and Right to Rent by link, a tenant£3.49

    A certified check of a British or Irish passport, or their Home Office share code, with the ID check. For lets in England.

  • Monitoring until completion: the first 6 weeks are included; a longer job’s watch is extended at 10p a person a week.
  • Starting, changing and reading transactions, quotes, the company lookup, records and webhooks have no charge, and each job’s certificate is included.
  • A check that fails is not charged, and nothing runs without the credit for it.
  • Credit tops up from £5 on the pricing page and never expires.

How to start

  1. 1

    Sign up, or sign in

    Use your firm’s Certaby account, or make one. Set your firm’s name in Settings: it is printed on every certificate.

  2. 2

    Make your test key

    In Settings, open Developers and make a test key. It runs 50 real checks free, so you can build against real answers.

  3. 3

    Go live

    Top up your credit and make your live key. Every reply tells you the credit left.

Signed in already? Open Settings, Developers. Your first call can be GET /v1/transactions: it lists your transactions and has no charge.

Reference

The Certaby API reference

Every example below runs against the live API. Put your key in an environment variable first (export CERTABY_KEY=certaby_test_...), and start with your test key: it runs real checks and charges nothing.

Your key and the base address

Every call goes to https://api.certaby.com and carries your key in the X-Certaby-Key header. A live key starts certaby_; a test key starts certaby_test_.

Your transactions, newest first
curl https://api.certaby.com/v1/transactions \
  -H "X-Certaby-Key: $CERTABY_KEY"
  • Make your keys in Settings, Developers. An account has one live key and one test key. Each is shown once, when it is made; after that you see its last four characters.
  • A new key stops the old one straight away, and so does revoking it. In a team, the account’s owner manages the keys.
  • Keep the key on your server. Never put it in a web page or an app people can download.
  • Requests and replies are JSON. Amounts are in pence; dates are ISO 8601 in UTC.

A job, from start to certificate

A transaction is one job: a let, a sale, or a finance case such as a loan. It holds the people and the property, the checks chosen for them, their results and one signed certificate. It is an ordinary Certaby transaction, so your team also sees it under Transactions on certaby.com.

  1. 1

    Start it

    curl -X POST https://api.certaby.com/v1/transactions \
      -H "X-Certaby-Key: $CERTABY_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "type": "sale"
      }'

    type is letting, sale or finance (clients, borrowers and companies, with no property). The reply (201) gives the txnId you use from now on.

  2. 2

    Add the people, the property and the checks

    curl -X PATCH https://api.certaby.com/v1/transactions/$TXN_ID \
      -H "X-Certaby-Key: $CERTABY_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "reference": {
          "matterRef": "SALE-2208"
        },
        "property": {
          "postcode": "LS1 5HN",
          "addressLine1": "12 Park Row"
        },
        "parties": [
          {
            "partyId": "buyer-1",
            "role": "buyer",
            "kind": "individual",
            "name": "Jordan Ellis",
            "dob": "1991-04-12"
          },
          {
            "partyId": "seller-1",
            "role": "seller",
            "kind": "individual",
            "name": "Morgan Hale"
          }
        ],
        "selectedCheckKeys": ["sanctions", "pepAdverseMedia", "propertyPack", "monitoring"]
      }'
    parties
    Everyone in the job: role, kind (individual or corporate), name, and for a company its companyNumber. A date of birth (dob, YYYY-MM-DD) helps tell people with the same name apart. The list replaces the last one sent, so send everyone each time, each with the same partyId.
    property
    The postcode and first line (addressLine1). The postcode also sets which UK nation’s rules apply.
    selectedCheckKeys
    The checks to run, from the list below.
    reference
    Your own reference (matterRef), printed on the certificate.
    issuerName
    The firm this job’s certificate names, up to 200 characters. A platform that serves several firms (a lending platform with many lenders, say) puts each firm’s name on its own jobs. null goes back to your account’s firm name.

    Roles in each kind of job

    • Letting: tenant (tenant), landlord (landlord), guarantor (guarantor), payer (payer), rep (representative)
    • Property sale: buyer (buyer), seller (seller), payer (payer), rep (representative)
    • Finance and business: client (client), donor (donor or funder), director (director or owner), counterparty (counterparty), payer (payer), rep (representative)

    Check keys

    sanctions
    Sanctions screen of every named person and company.
    pepAdverseMedia
    PEP and adverse media, added to the sanctions screen (for a company, also its directors and owners). Brings in sanctions if it isn't chosen.
    pscWalk
    Company and owners, for each company with a companyNumber.
    kyb
    The KYB package, for each company with a companyNumber, in place of company and owners.
    propertyPack
    The property risk pack, for the property (it needs a postcode).
    idv
    The ID check: by link with idMethods { "idv": "link" }, or recorded by your firm.
    rightToRent
    Right to Rent, for tenants on a let in England.
    monitoring
    Monitoring until completion: everyone named, from the run.
    riskRecord
    The customer's risk and source of funds record, filled in with POST /risk.
  3. 3

    See the price before anything runs

    curl https://api.certaby.com/v1/transactions/$TXN_ID/quote \
      -H "X-Certaby-Key: $CERTABY_KEY"

    The reply lists each check (lineItems), the totalPence and your balancePence. The total assumes every check runs; a run charges only checks that are new, changed or failed since the last run.

  4. 4

    Run it

    curl -X POST https://api.certaby.com/v1/transactions/$TXN_ID/run \
      -H "X-Certaby-Key: $CERTABY_KEY"
    ok
    True when the run went ahead. Check it on every reply.
    totalChargedPence
    What this run took from your credit. A check that fails is not charged.
    balanceAfterPence
    Your credit after the run.
    certHash
    The certificate’s reference (see step 6).
    transaction
    The whole transaction, with its results.
    more
    A big job can run in parts: when the reply says "more": true, call run again with { "continue": true } until it doesn’t.

    Run it again after a change and only what is new or changed is checked and charged. Send { "recheck": true } to check everyone again, charged in full.

  5. 5

    Read the results

    transaction.results has one row per check and person: checkKey, partyId, checkId, status (success, partial, pending or error), costPence, ranAt, and for a screen findings, the number of possible matches by kind. A pending row is a step for your firm, such as a Right to Rent check to record.

    The full answer behind one row
    curl https://api.certaby.com/v1/transactions/$TXN_ID/checks/$CHECK_ID \
      -H "X-Certaby-Key: $CERTABY_KEY"

    GET /v1/transactions/{id}/results.csv gives every name and what each check found, as a spreadsheet for your records.

  6. 6

    The certificate

    Each run issues one signed certificate for the whole job. Anyone can check it at https://certaby.com/verify/{certHash}, which shows the verdict, the lists checked and the date, and no personal details. transaction.cert.summaryVerdict is PASS, REVIEW or FAIL.

    The certificate is re-issued, with a new certHash, when a record is added or the watch changes. Your team can download the PDF from the transaction on certaby.com. The firm it names comes from your account; set it once:

    curl -X PUT https://api.certaby.com/v1/transactions/firm \
      -H "X-Certaby-Key: $CERTABY_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "firmName": "Oakwood Lettings"
      }'

A job in one call

A suite starts the job, chooses its checks from what you send, runs it and issues the certificate, all in one call. After that it is an ordinary transaction, with a txnId like any other.

curl -X POST https://api.certaby.com/v1/suites/letting/check \
  -H "X-Certaby-Key: $CERTABY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": {
      "matterRef": "LET-1042"
    },
    "property": {
      "postcode": "LS1 5HN",
      "addressLine1": "Flat 3, 12 Park Row"
    },
    "parties": [
      {
        "role": "tenant",
        "kind": "individual",
        "name": "Jordan Ellis",
        "dob": "1991-04-12"
      },
      {
        "role": "landlord",
        "kind": "corporate",
        "name": "Park Row Homes Ltd",
        "companyNumber": "12345678"
      }
    ],
    "pep": true
  }'
  • {suite} is letting, sale or finance, with the roles listed in step 2 above, and up to 50 parties.
  • Everyone named gets the sanctions screen.
  • PEP and adverse media: "pep": true or false. Without it, PEP is on for a property sale and on for a finance and business and off for a letting.
  • A company with a companyNumber gets the company and owners check; in a finance job, "kyb": true gives it the KYB package instead.
  • A letting or sale with a property.postcode gets the property risk pack ("propertyPack": false leaves it out).
  • reference and issuerName work as in step 2 above.
  • The reply is the run’s reply, plus txnId and the checks chosen. Short of credit, the job is kept as a draft: top up and run it by its txnId.

GET /v1/suites lists each suite, what it runs and the price of its usual job.

ID and Right to Rent by link

The person checks their own ID on their phone, from a link. For a tenant on a let in England, the same link can also check their right to rent. The result and the identity service’s PDF report come back to the transaction.

  1. 1

    Choose “by link” on the transaction

    For a tenant, ID and Right to Rent together:

    curl -X PATCH https://api.certaby.com/v1/transactions/$TXN_ID \
      -H "X-Certaby-Key: $CERTABY_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "selectedCheckKeys": ["sanctions", "rightToRent"],
        "idMethods": {
          "rightToRent": "link"
        }
      }'

    For anyone else (a buyer, a landlord, a borrower), the ID check: {"selectedCheckKeys":["sanctions","idv"],"idMethods":{"idv":"link"}}. For a tenant’s ID alone, use "idMethods": { "rightToRent": "self", "tenantId": "link" }.

    The link’s page names your firm, so your firm’s name must be set first (PUT /v1/transactions/firm, or Settings on certaby.com).

  2. 2

    Make the link

    curl -X POST https://api.certaby.com/v1/transactions/$TXN_ID/digital-id \
      -H "X-Certaby-Key: $CERTABY_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "partyId": "tenant-1",
        "email": "jordan.ellis@example.com"
      }'

    The reply (201) has the digitalId: its requestId, status (waiting), the startUrl for the person, expiresAt and chargedPence. The price is taken from your credit now and given back if the check ends without a result. Asking again for the same person while their link is open gives the same link ("already": true) and charges nothing more. Add "kind": "id" to check a tenant’s ID alone.

  3. 3

    The person checks their ID

    Certaby emails the person their link, in your firm’s name. startUrl is the same link, if you also want to show it in your product: it opens a page on certaby.com in your firm’s name, then the check itself. The link works for 7 days.

  4. 4

    Follow it

    Read where each link stands with GET /v1/transactions/{id}/digital-id, or ask the identity service for the latest with POST .../digital-id/{requestId}/refresh.

    waiting
    Link sent, waiting for them
    processing
    Finished, results being checked
    verified
    Verified
    review
    Needs your review
    locked_out
    Could not verify them (too many attempts). No charge
    expired
    Not finished in time. No charge
    cancelled
    Cancelled. No charge
    blocked
    The identity service refused this person. No charge

    No charge for locked out, expired, cancelled and blocked: the price goes back to your credit.

  5. 5

    Finish it

    • review: read the report (GET .../report), then accept it with a sentence on why: POST .../accept { "note": "..." }.
    • Right to Rent: the Home Office asks you to make sure the photo and date of birth in the report are the tenant’s. Confirm it with POST .../photo { "confirmed": true }; the Right to Rent is recorded then.
    • A link not finished can be cancelled (DELETE .../digital-id/{requestId}), and the charge comes back.

Monitoring until completion

Add monitoring to the checks and the run puts everyone named on a daily watch: they are screened against the sanctions lists again every morning until you mark the job complete. The first 6 weeks are included. A new match is a watch.alert webhook event.

  • POST /v1/transactions/{id}/monitor starts the watch now, or adds anyone new to it.
  • Longer jobs: extend by 2, 4, 8 and 12 weeks at 10p a person a week, up to 52 weeks in all.
  • DELETE /v1/transactions/{id}/monitor when the job completes: the watch stops.
Extend the watch
curl -X PUT https://api.certaby.com/v1/transactions/$TXN_ID/monitor \
  -H "X-Certaby-Key: $CERTABY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "weeks": 4
  }'

Webhooks

Give us one https address and we post each event to it as it happens. Set it in Settings, Developers, or with the API:

curl -X PUT https://api.certaby.com/v1/webhooks \
  -H "X-Certaby-Key: $CERTABY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example/webhooks/certaby"
  }'

A new address, or "rotateSecret": true, returns the signing secret once. POST /v1/webhooks/test sends a test event, and GET /v1/webhooks/deliveries shows how the newest ones went.

transaction.matches
A run found something to review: the rows, with their checkId.
certificate.reissued
A certificate has a new version: certHash, previousCertHash and verifyUrl.
watch.alert
The daily watch found a new match for someone in a job: txnId, entityName and what is new.
ping
A test event.
An event
{
  "id": "evt_6f1c0a9b2e4d4c7a8b3e5f10",
  "type": "transaction.matches",
  "createdAt": "2026-10-28T14:05:12.000Z",
  "data": {
    "txnId": "b7e3c2a0-5d1f-4e8a-9c6b-2f4d8a1e0c37",
    "type": "letting",
    "reference": "LET-1042",
    "matches": [
      { "checkId": "3b8f2c1e-9a4d-4f7b", "checkKey": "sanctions", "partyId": "party-1", "findings": { "sanctions": 1 } }
    ],
    "certHash": "9c41d2e07ab35f6e1d88",
    "detail": "GET /v1/transactions/b7e3c2a0-5d1f-4e8a-9c6b-2f4d8a1e0c37/checks/{checkId} for each match"
  }
}

Check that an event came from us

Each event comes with Certaby-Event-Id and Certaby-Event-Type headers (the same id and type as in the body), from the user agent Certaby-Webhooks/1.0, and a Certaby-Signature header: t=<unix seconds>,v1=<signature>, where the signature is the HMAC-SHA256, in hex, of the time, a full stop and the body, with your signing secret as the key. Check it against the body exactly as it arrived, before you parse it:

Node
import { createHmac, timingSafeEqual } from 'node:crypto';

// header: the Certaby-Signature header, "t=<unix seconds>,v1=<hex>"
// raw:    the request body exactly as it arrived (a string), before JSON.parse
// secret: your signing secret from Settings, Developers
export function isFromCertaby(header, raw, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.trim().split('=')));
  const t = Number(parts.t);
  // Refuse anything signed more than five minutes ago (each try is signed when it is sent).
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = createHmac('sha256', secret).update(`${t}.${raw}`).digest();
  const given = Buffer.from(parts.v1 ?? '', 'hex');
  return given.length === expected.length && timingSafeEqual(given, expected);
}
  • Answer with any 2xx status within 10 seconds. We don’t follow redirects.
  • Anything else is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, then marked as not delivered. Events are kept for 30 days.
  • An event can arrive more than once: use its id to skip repeats. Events from the test key say "test": true.

Errors

Every refusal says what to do in plain words. Most look like { "ok": false, "reason": "...", "message": "..." }; the key and creating or reading a transaction answer { "error": { "code": "...", "message": "..." } }. Read reason or error.code, and show message to your user if it helps.

StatusWhat it means
400Something in the request needs fixing; message says what.
401No key (missing_key), a key we don’t know (invalid_key), or one that was replaced or revoked (revoked_key).
402Not enough credit (insufficient_credit). Nothing ran and nothing was charged. The reply has topUpUrl, https://certaby.com/pricing#credit-packs; a run also says neededPence and balancePence.
403The key can’t do this: a route outside the Certaby API (certaby_key), or a live key before paid calls are open (not_open_yet).
404No such route, or no transaction, check or link with that id on your account.
409Not in this state: a test transaction run with the live key or the other way round, a link before your firm’s name is set, or a check that has already moved on.
429The test key’s free real checks are used up, or too few are left for this run (test_limit).
502, 503A service we rely on didn’t answer just now. Try again in a minute.

Credit

Checks are paid from your Certaby credit, the same balance your team uses on the website. Every reply carries X-Certaby-Credit-Balance-Pence: the credit left, in pence. Top up at certaby.com/pricing#credit-packs. Nothing runs without the credit for it, so a job is never half charged.

The test key

  • Real checks on real data, with nothing charged: 50 free real checks for your account, once.
  • PEP and adverse media give a labelled test answer.
  • Certificates name the issuer as TEST, not for compliance.
  • A test key runs the transactions it made; your live key runs its own.
  • ID links go to the identity service’s practice service: nobody is checked or emailed, and nothing is charged.
  • Monitoring is for live jobs.

Every route

All under https://api.certaby.com. Routes marked “paid” take credit at the prices above; the rest have no charge.

Transactions

  • POST/v1/transactionsStart a transaction: { "type": "letting" | "sale" | "finance" }.
  • GET/v1/transactionsYour transactions, newest first (?limit=1-200, ?after=nextCursor).
  • GET/v1/transactions/{id}One transaction, with its results and certificate.
  • PATCH/v1/transactions/{id}Set the parties, property, reference, checks and how ID is done.
  • GET/v1/transactions/{id}/quoteWhat the chosen checks cost, and your balance.
  • POST/v1/transactions/{id}/runRun the checks and issue the certificate.paid
  • GET/v1/transactions/{id}/checks/{checkId}The full answer behind one result row.
  • GET/v1/transactions/{id}/results.csvEvery name and what each check found, as a spreadsheet.
  • GET/v1/transactions/company-lookupA company and the people its check would screen (?number=), free.

Suites (a job in one call)

  • GET/v1/suitesEach suite, what it runs and its usual price.
  • POST/v1/suites/{suite}/checkStart, fill in and run a letting, sale or finance job in one call.paid

ID and Right to Rent by link

  • POST/v1/transactions/{id}/digital-idMake a link for a person: { partyId, email }.paid
  • GET/v1/transactions/{id}/digital-idWhere each link on the transaction stands.
  • POST/v1/transactions/{id}/digital-id/{requestId}/refreshRead the latest from the identity service now.
  • DELETE/v1/transactions/{id}/digital-id/{requestId}Cancel a link the person has not finished (the charge comes back).
  • GET/v1/transactions/{id}/digital-id/{requestId}/reportThe PDF report (a redirect to a download link that lasts 5 minutes).
  • POST/v1/transactions/{id}/digital-id/{requestId}/acceptAccept a check that needs review: { "note": "why" }.
  • POST/v1/transactions/{id}/digital-id/{requestId}/photoConfirm the photo is the tenant's (Right to Rent): { "confirmed": true }.
  • POST/v1/transactions/{id}/digital-id/{requestId}/report/deleteDelete the report once you have filed your own copy: { "confirmed": true }.

Monitoring until completion

  • POST/v1/transactions/{id}/monitorStart the daily watch now, or add anyone new to it.
  • PUT/v1/transactions/{id}/monitorExtend the watch: { "weeks": 2 | 4 | 8 | 12 }.paid
  • DELETE/v1/transactions/{id}/monitorThe job is complete: stop the watch.

Records kept with the job

  • POST/v1/transactions/{id}/idvRecord an ID check your firm did itself, free.
  • POST/v1/transactions/{id}/right-to-rentRecord a Right to Rent check your firm did itself, free.
  • POST/v1/transactions/{id}/riskA customer's risk and source of funds record, free.
  • POST/v1/transactions/{id}/evidenceA link to upload an evidence file for a record.
  • GET/v1/transactions/{id}/evidence/{fileId}Download an evidence file.

Your firm and webhook

  • GET/v1/transactions/firmThe firm name your certificates print.
  • PUT/v1/transactions/firmSet it: { "firmName": "...", "firmAddress": "..." }.
  • GET/v1/webhooksYour endpoint (the secret’s last four characters) and the events.
  • PUT/v1/webhooksSet it: { "url": "https://..." }; add "rotateSecret": true for a new secret.
  • DELETE/v1/webhooksStop sending.
  • POST/v1/webhooks/testSend a test event.
  • GET/v1/webhooks/deliveriesThe newest 50 events and how each went.

Questions while you build?

Write to hello@certaby.com with what you are building, and we’ll answer in plain words.