Data model
Data model
Lumen Sieve's core output is a nested, section-based JSON document that fuses data from every source it crawled into one confidence-scored record — not a flat object. Every field that has data carries its own source attribution and confidence inline, rather than through a separate lookup.
Envelope
GET /v1/profiles/{id} (once a resolve has finished) and the completed lookup/stream event both return the same shape:
{
"profile": { /* the sections below */ },
"profileId": "81f35e2b-e883-41ee-9d41-febc211bfe9e"
}A section Lumen Sieve found nothing for is an empty object ({}), not null and not omitted — so you can always safely access profile.reputation etc. without a null check, you just won't find any keys inside it.
The "field value" wrapper
Almost every leaf value in a profile is wrapped with its own provenance, instead of being a bare string or number:
{
"value": "Blispa Barbershop & Spa",
"metadata": {
"confidence": 0.995,
"sources": ["https://blispabarbershop.co.ke/", "https://blispabarbershop.co.ke/about"],
"last_verified": "2026-07-31T23:35:12Z"
}
}sources is every URL that contributed to this exact value — this is what a separate per-field attribution endpoint would otherwise give you, already inline. Collection fields (services, phones, hours, team members, …) carry the same metadata block per item instead of per top-level field.
Top-level sections
| Section | Shape | Contains |
|---|---|---|
business | { trade_name, description } | Name and a short description, each a field value |
services | { services: [...] } | Service line items — name, optional description, optional pricing |
products | { products: [...] } | Product line items — same shape as services |
contact | { website, phones, emails } | phones/emails are arrays of { number|address, purpose, metadata } |
location | { headquarters, branches? } | headquarters: { city, address, country_code, country, formatted_address, metadata } |
hours | { regular: { monday…sunday } } | Each day: { open, close, closed, metadata } |
payment | { methods? } | Accepted payment methods, when found |
team | { members, metadata } | members: [{ name, role, verified, metadata }] |
digital_presence | { social_profiles } | social_profiles: { platform: { url, handle, metadata } } |
financial | {} | Revenue/funding signals, when found |
reputation | {} | Review/rating platform data, when found |
positioning | { value_proposition, tagline, mission, target_market, differentiators } | LLM-synthesized from the business’s own site, verbatim-grounded |
news | {} | Press mentions, when found |
customers | { testimonials } | testimonials: [{ quote, author }] |
technology | { detected, by_category } | Detected web technologies (analytics, fonts, CMS, …) |
legal | {} | Registration/legal-entity data, when found |
brand | { logo_url } | Logo URL as a field value |
metadata | { generated_at, last_updated, confidence_score, completeness_score, sources_queried, sources_successful } | Whole-profile stats, not per-field |
Real example (trimmed)
{
"profile": {
"business": {
"trade_name": { "value": "Fresh Cuts Barbers and Salon",
"metadata": { "confidence": 0.70,
"sources": ["https://addagio.io/book/fresh-cuts-barbers-and-salon-nairobi", "..."],
"last_verified": "2026-08-01T00:28:49Z" } }
},
"contact": {
"phones": [{ "number": "+254723345667", "purpose": "main",
"metadata": { "confidence": 0.97, "sources": ["..."], "last_verified": "2026-08-01T00:29:10Z" } }]
},
"location": {
"headquarters": { "city": "Kindaruma Road, Kilimani", "address": "Nairobi",
"country_code": "", "country": "", "formatted_address": "Nairobi, Kindaruma Road, Kilimani",
"metadata": { "confidence": 0.62, "sources": ["..."], "last_verified": "..." } }
},
"digital_presence": {
"social_profiles": { "facebook": { "url": "https://facebook.com/freshcutsbarbersandsalon",
"handle": "freshcutsbarbersandsalon", "metadata": { "confidence": 0.9, "sources": ["..."], "last_verified": null } } }
},
"metadata": { "generated_at": "2026-08-01T00:30:38Z", "last_updated": "2026-08-01T00:30:38Z",
"confidence_score": 0.6275, "completeness_score": 0.7778, "sources_queried": 7, "sources_successful": 7 }
},
"profileId": "f6ac17d1-9fd1-4fe2-9009-5aab8a168dad"
}Confidence & completeness
Two scores live in profile.metadata, aggregated across the whole profile:
confidence_score— how much Lumen Sieve trusts what it found, weighted by source agreement and source trust.completeness_score— how much of the expected profile shape actually got filled in.
Each individual field's own metadata.confidence is the more precise signal for per-field decisions — use the whole-profile score to triage which profiles need a second look, and the per-field score to decide whether to trust one specific value.
sources array with two or more independent URLs is a stronger trust signal than a single high confidence number from one source — check both.Source attribution
A dedicated GET /v1/profiles/{id}/attribution/{fieldPath} endpoint is planned but not yet implemented (returns 501 today). In the meantime, every field's own metadata.sources and metadata.confidence — shown above — already carry that provenance inline; for most use cases you don't need a separate call.
Pagination
List endpoints return a standard paginated envelope. Pass nextCursor back as the cursor query parameter to fetch the next page.
{
"items": [ … ],
"totalCount": 142,
"hasMore": true,
"nextCursor": "eyJpZCI6Ij…"
}