vFound homeStart a free trial
DEVELOPERS

The vFound API and webhooks

Connect your lost and found to the systems you already run: property management, service desks, data warehouses or your own apps. Read and add found items, follow claims and lost reports, pull analytics, and get a signed webhook the moment anything happens.

Authentication

The API is included in vFound Pro. An owner or manager creates keys in Settings, API and webhooks, choosing what each key may do. The full key is shown once. Send it as a Bearer token:

curl https://vfound.io/api/v1/items \
  -H "Authorization: Bearer vf_live_xxxxxxxxxx_…"
ScopeAllowsEndpoints
items:readRead found items and categoriesGET /items, /items/{id}, /categories
items:writeAdd and update found itemsPOST /items, PATCH /items/{id}
claims:readRead claims, with the claimant’s contact detailsGET /claims, /claims/{id}
lost_reports:readRead guests’ lost reportsGET /lost-reports
analytics:readRead the analytics summaryGET /analytics

A key acts as the person who created it and stops working if that person no longer manages the venue. Each key can make 120 requests a minute. Base address: https://vfound.io/api/v1.

Items

RequestWhat it doesScope
GET/itemsItems, newest change first. Filters: status, updated_since (ISO 8601), page, per_page (up to 100).items:read
GET/items/{id}One item by its id.items:read
GET/categoriesThe category names an item can use.items:read
POST/itemsAdd a found item. It is published to your guest page, runs alerts and matching, and appears in your audit trail like one added by hand.items:write
PATCH/items/{id}Change details or status.items:write

Add an item

curl https://vfound.io/api/v1/items -X POST \
  -H "Authorization: Bearer $VFOUND_KEY" -H "Content-Type: application/json" \
  -d '{"title":"Black leather wallet","category":"Wallets & Purses","found_date":"2026-09-23",
       "location":"Front desk","colour":"Black","brand":"Fossil","tags":["leather","cards"],
       "photo":"data:image/jpeg;base64,…","photo_public":true}'

Fields: title and category are required. Optional: found_date, found_time (HH:MM), keep_until, location, description, colour, brand, tags, value_band (unknown, low, medium, high), notes (staff only), photo (a base64 data URL up to 8 MB) and photo_public.

The item object

{"id":"9b2f…","title":"Black leather wallet","status":"in_care","category":"Wallets & Purses",
 "found_date":"2026-09-23","found_time":"14:30","keep_until":"2026-10-21","location":"Front desk",
 "description":null,"colour":"Black","brand":"Fossil","tags":["leather","cards"],"value_band":"low",
 "photo_public":true,"photo_url":"https://vfound.io/uploads/…","created_at":"…","updated_at":"…"}

Status is one of in_care, claimed, expired, donated, disposed or trash. A PATCH may set in_care, expired, donated, disposed or trash; returns to a guest happen through claims.

Claims

RequestWhat it doesScope
GET/claimsClaims, newest change first. Filters: status, updated_since, page, per_page.claims:read
GET/claims/{id}One claim by its reference.claims:read
{"id":"k3f9x2a1b7c4d8e6","status":"ready_for_pickup","item":{"id":"9b2f…","title":"Black leather wallet"},
 "claimant":{"first_name":"Ana","last_name":"Ruiz","email":"ana@example.com","phone":null},
 "description":"…","place_lost":"Bar","date_lost":"2026-09-22","language":"es","created_at":"…","updated_at":"…"}

Claim status: unread, open, accepted, awaiting_information, ready_for_pickup, ready_for_shipment, shipped, closed or rejected. Claimants’ details are personal data: keep them only as long as you need them.

Lost reports

GET/lost-reports lists what guests reported losing, with status (open or closed), the reporter’s contact details, the language they used and any Find My link they shared. Scope: lost_reports:read.

Analytics

GET/analytics?days=30 returns the same summary as Reports: items logged and returned, return rate, median days to return, where items ended up, categories, hotspots, busiest days, team output, the trend and guest feedback. days is 7, 30, 90 or 365. Scope: analytics:read.

Webhooks

Add an HTTPS address in Settings, API and webhooks and choose events. vFound sends a JSON POST within seconds, retries failures for about three hours, and pauses an address after 15 failed deliveries in a row.

EventWhenData
item.createdA found item is addedItem
item.updatedIts status, title, location, category, keep until date or photo visibility changesItem
claim.createdA guest submits a claimClaim
claim.status_changedA claim moves to a new statusClaim
lost_report.createdA guest reports a lossLost report
{"id":"evt_…","type":"claim.created","created":"2026-09-23T14:30:00+00:00",
 "venue":{"code":"harbourhotel","name":"Harbour Hotel"},"data":{ …the claim… }}

Check the signature

Every delivery carries Vfound-Signature: t=<unix time>,v1=<hex>, where the hex is an HMAC SHA256 of t + "." + raw body with your endpoint’s secret. Reject deliveries older than five minutes.

// Node.js
const crypto = require("crypto");
function verified(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
// PHP
[$t, $v1] = array_map(fn ($p) => explode('=', $p, 2)[1], explode(',', $header));
$ok = abs(time() - (int) $t) < 300
    && hash_equals(hash_hmac('sha256', $t.'.'.$rawBody, $secret), $v1);

Headers also include Vfound-Event and a unique Vfound-Delivery id you can use to ignore repeats. Webhooks are sent only to public HTTPS addresses on port 443.

Errors

Errors are JSON with a message; validation errors add errors by field. 401 means no valid key, 403 a missing scope, a paused subscription or a plan without the API, 404 not found in your venue, 422 invalid input and 429 too many requests.

Questions or a use case we should support? Email us. We read every message.