BuilderVet API API

Dated NSW and QLD licence, company and regulatory evidence. Consult per-source coverage before interpreting an absent finding. No safety rating, phoenix detection, solvency prediction or complete DA coverage. Public evidence is free; paid value is monitoring, private work and exports. Legacy fields

Operations 27

GET /v1/coverage/{slug} Per-source check state and timestamps; never inferred from entity update time. #
POST /v1/check/{slug} Run a live check of the NSW licence, ASIC and three NSW registers for one builder. Returns at once; poll coverage until lastAttemptAt is at or after startedAt. Current sources are #
POST /v1/hbc-site HBC certificates on record for one NSW site address (street number, street and postcode). Covers certificates from licences BuilderVet has read; not proof that none exists. #
POST /v1/quote-match Check that the name, ABN or ACN, licence and site address from a quote point to one builder, and whether an HBC certificate for the site is on record. Nothing is stored. #
GET /v1/rewards Private watch-credit wallet, own invitation labels/status and bounded history; no cash balance or friend identity. #
GET /v1/rewards/invite Public validity and expiry only; no inviter identity or private label. #
POST /v1/rewards/invites Create a private single-person invitation; does not send email or messages. Obtain member intent before creating or sharing. #
POST /v1/rewards/redeem Spend one owned ready pass on one exact NSW builder. Requires explicit member choice; no swaps. Same credit and builder retries are idempotent. #
GET /v1/workspace Private account folders, watches and notifications; no-store. #
GET /v1/billing Paid-through rights, legacy rights and checkout availability. #
POST /v1/billing/checkout Verified account checkout; total AUD99 homeowner or AUD199 monthly professional. #
POST /v1/billing/portal Account-owned Customer Portal; invoices, billing details (business name, ABN, address), cancel professional renewal at period end. #
GET /v1/billing/invoices The account's Stripe invoices (latest 100, no drafts), reports bought, and a summary by Sydney calendar month. Stripe is the record; legacy purchases show without an invoice. #
POST /v1/billing/invoices/{invoiceId}/email Email a copy of one of the account's invoices to the sign-in address. Three a day per invoice. #
GET /v1/suggest Typeahead autocomplete — top 8 matches for a partial name, licence number, ABN or ACN. Edge-cached and fast enough to call per keystroke. #
GET /v1/search Search builders by name (optionally filtered by postcode). #
GET /v1/builder/{slug} Full risk profile for one builder. #
GET /v1/builder/{slug}/timeline Dated event timeline (licence changes, insolvency, disciplinary, etc). #
GET /v1/export Bulk export of every builder profile with licences, risk signals and events (used to build the static site; large response). #
POST /v1/waitlist Record explicit launch interest in a homeowner report or professional monitoring offer. #
GET /v1/referral Referral-ladder progress for a code (count only, never emails). #
POST /v1/watch Watch an address for register events (free alerts; explicit consent required). #
GET /v1/watch/unsubscribe One-click unsubscribe from alert emails (token from the email footer). #
GET /v1/me The signed-in account's profile, address watches, referral progress and purchased reports. #
POST /v1/auth/logout Revoke the current session token (idempotent). #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/buildervet-au:buildervet-api-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

buildervet-au-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: BuilderVet API
  version: "0.1.0"
  description: >
    Dated NSW and QLD licence, company and regulatory evidence. Consult per-source coverage
    before interpreting an absent finding. No safety rating, phoenix detection,
    solvency prediction or complete DA coverage. Public evidence is free; paid
    value is monitoring, private work and exports. Legacy fields stay compatible.
  x-collection-policy: Logged-out, public, robots-honoured, entity-not-personal by design.
servers:
  # Live production origin. The .com.au domain is owned by a third party —
  # do not reference it until we own a real domain (product is being renamed).
  - url: https://api.buildervet.au
paths:
  /v1/coverage/{slug}:
    get:
      operationId: getBuilderCoverage
      summary: Per-source check state and timestamps; never inferred from entity update time.
      parameters: [{name: slug, in: path, required: true, schema: {type: string}}]
      responses:
        "200": {description: "coverage entries with state finding, checked, recorded (dated value on file, no live check yet), not_checked, stale, or unavailable; lastAttemptAt, lastSuccessAt, nextDueAt, detail, sourceUrl"}
        "429": {description: Rate limit; Retry-After specifies seconds}
  /v1/check/{slug}:
    post:
      operationId: startBuilderCheck
      summary: Run a live check of the NSW licence, ASIC and three NSW registers for one builder. Returns at once; poll coverage until lastAttemptAt is at or after startedAt. Current sources are not re-fetched.
      parameters: [{name: slug, in: path, required: true, schema: {type: string}}]
      responses:
        "202": {description: "started; body has startedAt and poll path"}
        "404": {description: Unknown builder}
        "422": {description: Outside NSW coverage}
        "429": {description: Rate limit (6 per minute per client)}
        "503": {description: Check service unavailable or hourly source cap reached}
  /v1/hbc-site:
    post:
      operationId: hbcForSite
      summary: HBC certificates on record for one NSW site address (street number, street and postcode). Covers certificates from licences BuilderVet has read; not proof that none exists.
      requestBody: {required: true, content: {application/json: {schema: {type: object, properties: {address: {type: string}}}}}}
      responses:
        "200": {description: "checked, detail, certificates (certificateNo, siteAddress, issuedOn, licensee, contractorLicence, slug), coverage counts"}
        "400": {description: Body is not JSON}
  /v1/quote-match:
    post:
      operationId: matchQuote
      summary: Check that the name, ABN or ACN, licence and site address from a quote point to one builder, and whether an HBC certificate for the site is on record. Nothing is stored.
      requestBody: {required: true, content: {application/json: {schema: {type: object, properties: {name: {type: string}, abn: {type: string}, acn: {type: string}, licence: {type: string}, address: {type: string}}}}}}
      responses:
        "200": {description: "verdict (match, mismatch, not_found, name_only, incomplete), builder, identifiers with detail, hbc site result, limits"}
        "400": {description: Body is not JSON}
        "429": {description: Rate limit}
  /v1/rewards:
    get:
      operationId: getMemberRewards
      summary: Private watch-credit wallet, own invitation labels/status and bounded history; no cash balance or friend identity.
      security: [{bearerSession: []}]
      responses:
        "200": {description: "policy, wallet trial/annual counts (ready, pending, used, expired, reversed), confirmed paidFriends progress, latest 100 invitations and credits"}
        "401": {description: Sign in required}
        "403": {description: Email verification required}
        "503": {description: Referral programme disabled}
  /v1/rewards/invite:
    get:
      operationId: checkMemberInvitation
      summary: Public validity and expiry only; no inviter identity or private label.
      parameters: [{name: token, in: query, required: true, schema: {type: string, pattern: '^[a-f0-9]{32}$'}}]
      responses:
        "200": {description: "valid boolean and optional expiresAt; used/expired/ineligible links return valid false"}
        "503": {description: Referral programme disabled}
  /v1/rewards/invites:
    post:
      operationId: createMemberInvitation
      summary: Create a private single-person invitation; does not send email or messages. Obtain member intent before creating or sharing.
      security: [{bearerSession: []}]
      requestBody: {required: true, content: {application/json: {schema: {type: object, properties: {label: {type: string, maxLength: 80}}}}}}
      responses:
        "200": {description: "invite with id, private label, URL, createdAt, expiresAt, status"}
        "400": {description: Invalid label or body}
        "401": {description: Sign in required}
        "403": {description: Email verification required}
        "409": {description: Daily or lifetime invite cap reached}
        "503": {description: Referral programme disabled}
  /v1/rewards/redeem:
    post:
      operationId: redeemMemberWatchCredit
      summary: Spend one owned ready pass on one exact NSW builder. Requires explicit member choice; no swaps. Same credit and builder retries are idempotent.
      security: [{bearerSession: []}]
      requestBody: {required: true, content: {application/json: {schema: {type: object, required: [creditId, entitySlug], properties: {creditId: {type: string}, entitySlug: {type: string}}}}}}
      responses:
        "200": {description: "ok, entitySlug, expiresAt; existing workspace API provisions the saved baseline and watch, with server repair"}
        "400": {description: Invalid credit or builder}
        "401": {description: Sign in required}
        "403": {description: Email verification required}
        "404": {description: Credit or builder not found}
        "409": {description: "Pending, expired, reversed, already-used credit; active equal-or-longer watch; or checkout in progress"}
        "422": {description: Builder outside NSW}
        "503": {description: Referral programme disabled}
  /v1/workspace:
    get:
      operationId: getWorkspace
      summary: Private account folders, watches and notifications; no-store.
      security: [{bearerSession: []}]
      responses:
        "200": {description: Account-owned workspace}
        "401": {description: Sign in required}
  /v1/billing:
    get:
      operationId: getBilling
      summary: Paid-through rights, legacy rights and checkout availability.
      security: [{bearerSession: []}]
      responses:
        "200": {description: Billing summary}
        "401": {description: Sign in required}
  /v1/billing/checkout:
    post:
      operationId: createMonitoringCheckout
      summary: Verified account checkout; total AUD99 homeowner or AUD199 monthly professional.
      security: [{bearerSession: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [product]
              properties:
                product: {type: string, enum: [homeowner, professional]}
                entitySlug: {type: string, description: Required for homeowner}
      responses:
        "200": {description: Stripe hosted checkout URL}
        "403": {description: Email proof required}
        "409": {description: Existing access or confirming payment; do not pay again}
        "503": {description: Paid release not enabled}
  /v1/billing/portal:
    post:
      operationId: openBillingPortal
      summary: Account-owned Customer Portal; invoices, billing details (business name, ABN, address), cancel professional renewal at period end.
      security: [{bearerSession: []}]
      responses:
        "200": {description: Stripe portal URL}
        "401": {description: Sign in required}
  /v1/billing/invoices:
    get:
      operationId: listInvoices
      summary: The account's Stripe invoices (latest 100, no drafts), reports bought, and a summary by Sydney calendar month. Stripe is the record; legacy purchases show without an invoice.
      security: [{bearerSession: []}]
      responses:
        "200": {description: "{customer, invoices, reports, months, hasMore, invoicesError, portal, emailTo}"}
        "401": {description: Sign in required}
  /v1/billing/invoices/{invoiceId}/email:
    post:
      operationId: emailInvoice
      summary: Email a copy of one of the account's invoices to the sign-in address. Three a day per invoice.
      security: [{bearerSession: []}]
      parameters:
        - {name: invoiceId, in: path, required: true, schema: {type: string, pattern: "^in_[A-Za-z0-9]+$"}}
      responses:
        "200": {description: "{sent: true, to}"}
        "401": {description: Sign in required}
        "404": {description: No such invoice on this account}
        "429": {description: Daily limit for this invoice reached}
        "502": {description: Email provider refused; try again}
  /v1/suggest:
    get:
      operationId: suggestBuilders
      summary: Typeahead autocomplete — top 8 matches for a partial name, licence number, ABN or ACN. Edge-cached and fast enough to call per keystroke.
      parameters:
        - { name: q, in: query, required: true, schema: { type: string, minLength: 2, maxLength: 80 } }
      responses:
        "200": { description: Suggestions (empty results for queries under 2 chars), content: { application/json: { schema: { $ref: "#/components/schemas/SearchResults" } } } }
  /v1/search:
    get:
      operationId: searchBuilders
      summary: Search builders by name (optionally filtered by postcode).
      parameters:
        - { name: q, in: query, required: true, schema: { type: string, minLength: 2 } }
        - { name: postcode, in: query, required: false, schema: { type: string } }
      responses:
        "200": { description: Matches, content: { application/json: { schema: { $ref: "#/components/schemas/SearchResults" } } } }
        "400": { description: Query shorter than 2 characters }
  /v1/builder/{slug}:
    get:
      operationId: getBuilder
      summary: Full risk profile for one builder.
      parameters: [ { name: slug, in: path, required: true, schema: { type: string } } ]
      responses:
        "200": { description: Profile, content: { application/json: { schema: { $ref: "#/components/schemas/BuilderProfile" } } } }
        "404": { description: Not found }
  /v1/builder/{slug}/timeline:
    get:
      operationId: getBuilderTimeline
      summary: Dated event timeline (licence changes, insolvency, disciplinary, etc).
      parameters: [ { name: slug, in: path, required: true, schema: { type: string } } ]
      responses:
        "200": { description: Timeline, content: { application/json: { schema: { $ref: "#/components/schemas/Timeline" } } } }
  /v1/export:
    get:
      operationId: exportAll
      summary: Bulk export of every builder profile with licences, risk signals and events (used to build the static site; large response).
      responses:
        "200":
          description: All builders
          content:
            application/json:
              schema:
                type: object
                properties:
                  count: { type: integer }
                  builders: { type: array, items: { $ref: "#/components/schemas/BuilderProfile" } }
  /v1/waitlist:
    post:
      operationId: joinWaitlist
      summary: Record explicit launch interest in a homeowner report or professional monitoring offer.
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [email, intent, offerCode, consent], properties: { email: { type: string, format: email, maxLength: 254 }, intent: { type: string, enum: [report, monitor] }, offerCode: { type: string, enum: [homeowner, professional] }, consent: { type: boolean, enum: [true] }, entitySlug: { type: string, pattern: "^[a-z0-9-]{1,80}$" }, ts: { type: string, description: "Form-render timestamp for bot filtering; not used as the stored consent timestamp." }, website: { type: string, description: "Leave blank; honeypot." }, session: { type: string } } } } }
      responses:
        "200": { description: "Check saved=true before showing success; filtered traffic returns saved=false.", content: { application/json: { schema: { type: object, required: [ok, saved], properties: { ok: { type: boolean }, saved: { type: boolean } } } } } }
        "400": { description: Invalid email, intent/offer pair, consent or entitySlug }
  /v1/referral:
    get:
      operationId: referralStatus
      summary: Referral-ladder progress for a code (count only, never emails).
      parameters:
        - { name: code, in: query, required: true, schema: { type: string, pattern: "^[a-z0-9]{8}$" } }
      responses:
        "200": { description: OK, content: { application/json: { schema: { $ref: "#/components/schemas/ReferralStatus" } } } }
        "400": { description: Invalid code }
  /v1/watch:
    post:
      operationId: watchAddress
      summary: Watch an address for register events (free alerts; explicit consent required).
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [email, address, consent], properties: { email: { type: string, format: email, maxLength: 254 }, address: { type: string, minLength: 5, maxLength: 160 }, postcode: { type: string, pattern: "^\\d{4}$" }, builderName: { type: string, maxLength: 120 }, consent: { type: boolean }, ref: { type: string, pattern: "^[a-z0-9]{8}$" } } } } }
      responses:
        "200": { description: "OK; returns the email's referral code and referral count.", content: { application/json: { schema: { $ref: "#/components/schemas/ReferralStatus" } } } }
        "400": { description: Invalid email, address or missing consent }
  /v1/watch/unsubscribe:
    get:
      operationId: unsubscribeWatch
      summary: One-click unsubscribe from alert emails (token from the email footer).
      parameters:
        - { name: token, in: query, required: true, schema: { type: string, pattern: "^[a-z0-9]{16}$" } }
      responses:
        "200": { description: "Always OK (HTML confirmation; never confirms token existence)." }
  /v1/auth/request-link:
    post:
      operationId: requestSignInLink
      summary: Email a one-time passwordless sign-in link (always returns ok; no account enumeration).
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [email], properties: { email: { type: string, format: email, maxLength: 254 } } } } }
      responses:
        "200": { description: "Always {ok:true}. The link expires in 15 minutes and works once." }
  /v1/auth/verify:
    get:
      operationId: verifySignInLink
      summary: Exchange a magic-link token for a session (browser redirect; token from the sign-in email).
      parameters:
        - { name: token, in: query, required: true, schema: { type: string, pattern: "^[0-9a-f]{64}$" } }
      responses:
        "302": { description: "Redirects to the account page with the bearer session token in the URL fragment (or #error=expired)." }
  /v1/me:
    get:
      operationId: getAccount
      summary: The signed-in account's profile, address watches, referral progress and purchased reports.
      security: [{ bearerSession: [] }]
      responses:
        "200": { description: "Account view.", content: { application/json: { schema: { $ref: "#/components/schemas/AccountView" } } } }
        "401": { description: Missing, expired or revoked session token }
  /v1/auth/logout:
    post:
      operationId: logout
      summary: Revoke the current session token (idempotent).
      security: [{ bearerSession: [] }]
      responses:
        "200": { description: "Always {ok:true}." }
components:
  securitySchemes:
    bearerSession:
      type: http
      scheme: bearer
      description: Session token minted by /v1/auth/verify (64-char hex).
  schemas:
    AccountView:
      type: object
      properties:
        email: { type: string }
        createdAt: { type: string }
        watches: { type: array, items: { type: object, properties: { address: { type: string }, postcode: { type: string, nullable: true }, builder: { type: string, nullable: true }, active: { type: integer }, createdAt: { type: string } } } }
        refCode: { type: string, nullable: true }
        referrals: { type: integer }
        purchases: { type: array, items: { type: object, properties: { kind: { type: string }, entitySlug: { type: string, nullable: true }, createdAt: { type: string } } } }
    ReferralStatus:
      type: object
      properties:
        ok: { type: boolean }
        refCode: { type: string, description: "This email's shareable referral code." }
        referrals: { type: integer, description: "Distinct emails referred by this code." }
    SearchResults:
      type: object
      properties:
        query: { type: string }
        results:
          type: array
          items:
            type: object
            properties: { slug: { type: string }, displayName: { type: string }, postcode: { type: string, nullable: true }, status: { type: string, nullable: true } }
    Licence:
      type: object
      properties: { licenceNo: { type: string }, class: { type: string }, status: { type: string }, grantedOn: { type: string }, expiresOn: { type: string }, suburb: { type: string }, postcode: { type: string } }
    BuilderProfile:
      type: object
      properties:
        slug: { type: string }
        displayName: { type: string }
        legalName: { type: string }
        abn: { type: string, nullable: true }
        acn: { type: string, nullable: true }
        asicStatus: { type: string, nullable: true }
        licences: { type: array, items: { $ref: "#/components/schemas/Licence" } }
        riskSignals: { type: array, items: { type: object, properties: { signal: { type: string }, severity: { type: string } } } }
        firstObservedByBuilderCheck: { type: string, description: "When BuilderVet's archive first captured this entity." }
    Timeline:
      type: object
      properties:
        slug: { type: string }
        events:
          type: array
          items:
            type: object
            properties:
              kind: { type: string }
              severity: { type: string, enum: [info, watch, risk] }
              summary: { type: string }
              occurredOn: { type: string, nullable: true }
              sourceId: { type: string }
              onlyAvailableHere: { type: boolean, description: "True when upstream has deleted this record; BuilderVet retained a dated copy. This does not imply exclusive access." }