Public API

v1 stableNo API key

Developer platform

Build on confirmedcompany evidence.

Read-only JSON and embed widgets — the same trust rules as a Hansala company page. Nothing pending. Nothing private.

Base URL

linken-tau.vercel.app/api/v1

GET · companies/{slug}

200
{
  "slug": "example-architecture",
  "name": "Example Architecture",
  "category": "Architecture",
  "city": "Berlin",
  "country": "Germany",
  "website": "https://example.com",
  "verified": true,
  "claimed": true,
  "accepting_clients": true,
  "trust_level": "established",
  "stats": {
    "confirmed_partners": 6,
    "confirmed_references": 4,
    "ongoing_references": 2,
    "confirmed_case_studies": 3
  },
  "assessment": {
    "would_work_again_yes": 9,
    "would_work_again_total": 10,
    "top_strengths": [
      { "key": "reliability", "label": "Reliability", "count": 8 },
      { "key": "deadlines", "label": "Deadlines", "count": 6 }
    ]
  },
  "profile_url": "https://linken-tau.vercel.app/c/example-architecture",
  "generated_at": "2026-07-19T18:00:00.000Z"
}

Surface

REST · JSON

Read-only GET

Evidence

Confirmed only

Same as public profiles

Cache

5 minutes

stale-while-revalidate

Access

Open CORS

No API key required

01

Overview

The Public API exposes the same confirmed facts you see on a Hansala company page — partners, references, case studies, and assessment aggregates.

Confirmed only

Never pending invites, private emails, or confirmation tokens.

Edge-cache friendly

public, s-maxage=300, stale-while-revalidate=3600. Errors are no-store.

Open CORS

GET and OPTIONS from any origin — drop into any frontend.

No API key

Public GETs are open. Trust comes from confirmation rules, not secrets.

02

Quickstart

One request. Replace example-architecture with any public profile slug.

First call

Fetch a company in under a minute

GET
curl -sS "https://linken-tau.vercel.app/api/v1/companies/example-architecture" \
  -H "Accept: application/json"
03

Endpoints

Base path https://linken-tau.vercel.app/api/v1. All responses are JSON. Fields match the public contract in types.ts.

GET/api/v1/companies/{slug}
03.1

Company

Profile, trust level, confirmed stats, and assessment aggregates when anonymity rules allow.

Unclaimed companies return claimed: false with stats and assessment set to null.

Response fields

FieldTypeDescription
slugstringPublic URL slug.
namestringCompany display name.
categorystringPrimary category.
citystringCity.
countrystringCountry.
websitestringPublic website URL.
verifiedbooleanWhether the profile carries a verified mark.
claimedbooleanFalse for unclaimed directory stubs.
accepting_clientsbooleanOwner-set availability for new clients.
trust_level"member" | "established" | "trusted" | "pillar"Public Hansala Level (lowercase).
statsApiCompanyStats | nullConfirmed counts. Null when unclaimed.
stats.confirmed_partnersnumberAccepted mutual partnerships.
stats.confirmed_referencesnumberConfirmed finished service references.
stats.ongoing_referencesnumberConfirmed ongoing service references.
stats.confirmed_case_studiesnumberCase studies with client or partner confirmation.
assessmentApiAssessment | nullNull until would_work_again_total ≥ 3 (anonymity rule).
assessment.would_work_again_yesnumberClients who would work again.
assessment.would_work_again_totalnumberTotal would-work-again answers.
assessment.top_strengthsApiStrength[]Top strengths, highest count first.
assessment.top_strengths[].keystringCatalog key, e.g. "reliability".
assessment.top_strengths[].labelstringHuman label.
assessment.top_strengths[].countnumberHow many assessments selected this strength.
profile_urlstringAbsolute URL to the public profile.
generated_atstringISO-8601 generation timestamp.
curl -sS "https://linken-tau.vercel.app/api/v1/companies/example-architecture" \
  -H "Accept: application/json"
JSON
{
  "slug": "example-architecture",
  "name": "Example Architecture",
  "category": "Architecture",
  "city": "Berlin",
  "country": "Germany",
  "website": "https://example.com",
  "verified": true,
  "claimed": true,
  "accepting_clients": true,
  "trust_level": "established",
  "stats": {
    "confirmed_partners": 6,
    "confirmed_references": 4,
    "ongoing_references": 2,
    "confirmed_case_studies": 3
  },
  "assessment": {
    "would_work_again_yes": 9,
    "would_work_again_total": 10,
    "top_strengths": [
      { "key": "reliability", "label": "Reliability", "count": 8 },
      { "key": "deadlines", "label": "Deadlines", "count": 6 }
    ]
  },
  "profile_url": "https://linken-tau.vercel.app/c/example-architecture",
  "generated_at": "2026-07-19T18:00:00.000Z"
}
GET/api/v1/companies/{slug}/references
03.2

References

Confirmed service references only (max 50). Ongoing engagements are listed first.

Response fields

FieldTypeDescription
referencesApiReference[]Confirmed only, max 50. Ongoing first.
countnumberLength of references.
references[].client_namestringClient display name.
references[].client_slugstring | nullPublic slug when the client has a claimed Hansala profile.
references[].servicestringService described.
references[].started_yearstringStart year.
references[].ongoingbooleanStill active engagement.
references[].ended_yearstring | nullEnd year when finished.
references[].confirmed_atstringISO-8601 when the client confirmed.
curl -sS "https://linken-tau.vercel.app/api/v1/companies/example-architecture/references" \
  -H "Accept: application/json"
JSON
{
  "references": [
    {
      "client_name": "Nordwerk Holding",
      "client_slug": "nordwerk-holding",
      "service": "Shell construction",
      "started_year": "2023",
      "ongoing": true,
      "ended_year": null,
      "confirmed_at": "2024-11-02T09:14:00.000Z"
    },
    {
      "client_name": "Stadtwerke Spandau",
      "client_slug": null,
      "service": "Electrical fit-out",
      "started_year": "2021",
      "ongoing": false,
      "ended_year": "2022",
      "confirmed_at": "2023-03-18T14:22:00.000Z"
    }
  ],
  "count": 2
}
GET/api/v1/companies/{slug}/case-studies
03.3

Case studies

Case studies with client or partner confirmation (max 20).

Response fields

FieldTypeDescription
case_studiesApiCaseStudy[]Confirmed only, max 20.
countnumberLength of case_studies.
case_studies[].slugstringCase study slug.
case_studies[].titlestringTitle.
case_studies[].summarystringShort summary.
case_studies[].yearstringProject year.
case_studies[].locationstringLocation.
case_studies[].urlstringAbsolute URL to the case study page.
case_studies[].client_confirmedbooleanClient confirmation present.
case_studies[].confirmed_partnersApiCaseStudyPartner[]Partners who confirmed on this case.
case_studies[].confirmed_partners[].namestringPartner name.
case_studies[].confirmed_partners[].slugstringPartner public slug.
curl -sS "https://linken-tau.vercel.app/api/v1/companies/example-architecture/case-studies" \
  -H "Accept: application/json"
JSON
{
  "case_studies": [
    {
      "slug": "residence-berlin",
      "title": "Residence Berlin",
      "summary": "Private residence delivered with confirmed build and electrical partners.",
      "year": "2024",
      "location": "Berlin, Germany",
      "url": "https://linken-tau.vercel.app/c/example-architecture/case-studies/residence-berlin",
      "client_confirmed": true,
      "confirmed_partners": [
        { "name": "Example Build GmbH", "slug": "example-build" },
        { "name": "Example Elektro", "slug": "example-elektro" }
      ]
    }
  ],
  "count": 1
}
GET/api/v1/verify?domain={domain}
03.4

Verify (trust oracle)

One call to check a firm by website domain before you work with them. No API key. Unknown domains return found:false with HTTP 200.

Domain matching uses the same normalization as verification (exact host, www stripped). Only claimed companies are returned. Confirmed evidence only — never tokens or private fields.

Response fields

FieldTypeDescription
foundbooleanWhether a claimed company matched the domain. false is still HTTP 200.
company{ name, slug, profile_url } | nullMatched firm when found.
verifiedbooleanDomain verification flag.
verification_method"email_domain" | "dns_txt" | "meta_tag" | nullHow the domain was verified; null if not verified.
verified_sincestring | nullISO-8601 timestamp of domain verification.
trust_levelApiTrustLevel | nullPublic trust level from confirmed evidence only.
statsApiCompanyStats | nullConfirmed counts; null when not found.
assessmentApiAssessment | nullNull until ≥3 client assessment answers.
llm_md_urlstring | nullMarkdown snapshot URL for LLMs.
api_urlstring | nullCanonical Public API company URL.
curl -sS "https://linken-tau.vercel.app/api/v1/verify?domain=example.com" \
  -H "Accept: application/json"
JSON
{
  "found": true,
  "company": {
    "name": "Example Architecture",
    "slug": "example-architecture",
    "profile_url": "https://linken-tau.vercel.app/c/example-architecture"
  },
  "verified": true,
  "verification_method": "dns_txt",
  "verified_since": "2025-11-04T10:00:00.000Z",
  "trust_level": "established",
  "stats": {
    "confirmed_partners": 6,
    "confirmed_references": 4,
    "ongoing_references": 2,
    "confirmed_case_studies": 3
  },
  "assessment": {
    "would_work_again_yes": 9,
    "would_work_again_total": 10,
    "top_strengths": [
      { "key": "reliability", "label": "Reliability", "count": 8 }
    ]
  },
  "llm_md_url": "https://linken-tau.vercel.app/c/example-architecture/llm.md",
  "api_url": "https://linken-tau.vercel.app/api/v1/companies/example-architecture",
  "generated_at": "2026-07-19T18:00:00.000Z"
}
04

llms.txt & llm.md

Machine-readable entry points for AI agents that cite Hansala.

  • /llms.txt — site index: docs, API, profiles, trust model.
  • /c/{slug}/llm.md — markdown snapshot of a profile (same confirmed facts as the Public API). Linked from the HTML profile via rel="alternate".
05

Embeds

Drop an iframe on your site. Variants via ?variant=compact|badge|assessment|references and ?theme=light|dark. Links back with ?src=embed.

Prefer a visual configurator? Configure in your dashboard →

01

Badge

Compact verified mark. Default for existing embeds — no query param required.

Live preview

Snippet

<iframe src="https://linken-tau.vercel.app/embed/example-architecture" width="320" height="72" style="border:0" title="Verified on Hansala" loading="lazy"></iframe>
02

Assessment

Would-work-again rate and top strengths. Needs ≥3 client assessment answers on the profile.

Live preview

Snippet

<iframe src="https://linken-tau.vercel.app/embed/example-architecture?variant=assessment" width="320" height="120" style="border:0" title="Verified on Hansala" loading="lazy"></iframe>
03

References

Up to five confirmed client references. Empty profiles show a quiet fallback.

Live preview

Snippet

<iframe src="https://linken-tau.vercel.app/embed/example-architecture?variant=references" width="320" height="160" style="border:0" title="Verified on Hansala" loading="lazy"></iframe>
06

Agent API

Authenticated write surface for AI agents and integrations acting as your company. Create keys in Workspace → API.

What agents cannot do

Human confirmation is the product.

  • No confirm / accept / claim / respond endpoints — ever. An agent may build and invite; only a person with an email link may confirm.
  • No ownership transfer — blast radius stays human-only in the dashboard.
  • Keys are scoped to one company. Foreign company ids in the body are ignored or rejected. Public third-party reads use the Public API, not Agent routes.
  • Tokens for other parties, private_feedback, and foreign inquiries are never returned. Your own verify_token is available under verification:run.
  • Fields like verified, plan, slug, and claimed are not writable via PATCH /company.

Auth & scopes

Send Authorization: Bearer lk_…. The raw key is shown once at creation; we store only a SHA-256 hash. Revoke instantly from the dashboard.

ScopeAllows
readCompany, refs, cases, partnerships, inquiries, analytics, audit
content:writeContent, logo, profile fields, case-study partner tags
invites:sendSend confirmation / claim invites (never confirm)
team:manageTeam list, invites, remove members (accept via /join)
structure:manageGroups, subsidiaries, parent proposals, leave
settings:writeWidget settings, embed catalog, accepting clients
inquiries:managePatch inquiry status
verification:runVerify token/instructions + run checks

Presets in Workspace → API: Read only, Content manager, Full access. Rate limits: 120/min per key; invites 20/day per key.

Owner action → API endpoint

Everything an owner can click — except confirmations and ownership transfer — maps to Agent API.

Owner actionEndpointScope
Edit profile fieldsPATCH /companycontent:write
Refresh / upload logoPOST /logo/refresh · PUT /logocontent:write
Add / edit / delete referencePOST|PATCH|DELETE /referencescontent:write
Send reference invitePOST /references/{id}/inviteinvites:send
Case studies + partner tags…/case-studies · …/partnerscontent:write
Client confirmation requestPOST /client-confirmationsinvites:send
Invite partner (ghost)POST /partner-invitesinvites:send
Verify domain / backlinkGET|POST /verification…verification:run
Team invite / remove…/team…team:manage
Group / subsidiary / hierarchy…/group…structure:manage
Widgets & logo wallGET|PATCH /widget-settings · GET /widgetssettings:write
Triage inquiriesGET /inquiries · PATCH /inquiries/{id}read · inquiries:manage
Analytics / auditGET /analytics · GET /audit-logread

Agent setup script

Full lifecycle in six calls — create content, invite humans to confirm, configure the badge.

curl -sS -X PATCH "https://linken-tau.vercel.app/api/v1/agent/company" \
  -H "Authorization: Bearer lk_…" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"tagline":"Verified B2B network","city":"Berlin"}'
  • 1. PATCH /company — tagline & city
  • 2. POST /logo/refresh — pull logo from domain
  • 3. POST /references ×3 — pending evidence
  • 4. POST /references/{id}/invite — send confirms
  • 5. GET /widgets — pick embed snippet
  • 6. PATCH /widget-settings — logo wall exclusions

Endpoints

Base path https://linken-tau.vercel.app/api/v1/agent. Successes return { data: … }. Mutations are audit-logged. Team members expose opaque member_id (sha256 hex prefix) — never user_id.

GET/api/v1/agent/company
curl -sS -X GET "https://linken-tau.vercel.app/api/v1/agent/company" \
  -H "Authorization: Bearer lk_…" \
  -H "Accept: application/json"
POST/api/v1/agent/references
curl -sS -X POST "https://linken-tau.vercel.app/api/v1/agent/references" \
  -H "Authorization: Bearer lk_…" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"client_name":"CleanCo","service":"Office cleaning","started_year":"2024","ongoing":true}'
07

Errors

Failures use a consistent envelope. HTTP status matches the machine-readable code.

Status codes

200OK401unauthorized / invalid_key403insufficient_scope404not_found422invalid_request429rate_limited503service_unavailable500internal

Error body

FieldTypeDescription
error.code"not_found" | "invalid_request" | "internal"Machine-readable code.
error.messagestringHuman-readable message.
JSON
{
  "error": {
    "code": "not_found",
    "message": "Company not found."
  }
}
08

Versioning

A stable contract you can ship against.

Current

/api/v1/

Fields may be added. Never renamed, removed, or redefined.

Breaking changes

Ship under /api/v2/

Clients on v1 keep working. New major versions only when the meaning of a field must change.