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:

json
{
  "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:

json
{
  "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

SectionShapeContains
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)

A completed profile
json
{
  "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.

Tip: A 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.

Paginated envelope
json
{
  "items": [ … ],
  "totalCount": 142,
  "hasMore": true,
  "nextCursor": "eyJpZCI6Ij…"
}