obscyro

Api / Extract Concepts

/v1/extract/concepts

Extract clinical phrases from free text and resolve each one to ranked SNOMED concept candidates using embeddings and pgvector cosine search.

POST/v1/extract/concepts

Unlike /v1/normalize, which matches a single phrase you already isolated, extract/concepts takes a whole clinical note. It detects candidate spans (NER), embeds each one with a multilingual sentence-transformer, and resolves it against SNOMED descriptions via pgvector cosine similarity. Each span gets a status derived from the top cosine score and the margin to the second candidate, so you can decide which results are safe to auto-accept versus which need review.

Request body

Parameters

  • textstringrequired
    The clinical text to analyze. After trimming, must be at least 2 characters; otherwise the request returns 400 INVALID_INPUT.
  • language"en" | "fr" | "auto"optionaldefault: auto
    Language hint for span detection. auto lets the service infer it.

Response

One entry per detected span
{
  "concepts": [
    {
      "span": "fibrillation auriculaire",
      "candidates": [
        { "code": "49436004", "display": "Atrial fibrillation", "cosine": 0.91 },
        { "code": "40593004", "display": "Fibrillation", "cosine": 0.78 }
      ],
      "code": "49436004",
      "cosine": 0.91,
      "margin": 0.13,
      "concept_confidence": 0.91,
      "status": "resolved"
    }
  ]
}

Concept shape

  • spanstringoptional
    The exact text span detected in the note.
  • candidatesobject[]optional
    Top SNOMED candidates, each with code (SCTID), display, and cosine similarity.
  • codestring | nulloptional
    The chosen SCTID, or null when the span is unresolved.
  • cosinenumberoptional
    Cosine similarity of the top candidate.
  • marginnumberoptional
    Gap between the top and second candidate cosine. A small margin means the match is ambiguous.
  • concept_confidencenumberoptional
    Convenience score equal to the top cosine.
  • status"resolved" | "flag" | "unresolved"optional
    resolved: high cosine and margin; flag: matched but low confidence or margin, review recommended; unresolved: no candidate cleared the floor.

Examples

curl -X POST https://api.obscyro.com/v1/extract/concepts \
  -H "Authorization: Bearer $OBS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Patient suivi pour une fibrillation auriculaire sous anticoagulant.",
    "language": "fr"
  }'

Tips

  • Treat resolved spans as safe to map automatically; route flag and unresolved spans to human review.
  • Feed the returned spans and codes into /v1/extract/contexts to learn whether each concept is negated, historical, or about a family member.
  • The service uses a multilingual embedding model, so French and English notes both work without a separate model.

Errors

  • VALIDATION_ERROR

    Body did not pass schema validation (missing text, bad language).

  • INVALID_INPUT

    text was shorter than 2 characters after trimming.

  • INVALID_API_KEY

    Missing or invalid bearer token.

  • RATE_LIMITED

    Per-minute rate limit exceeded for this key.

  • NLP_UPSTREAM_ERROR

    The extraction service returned an error.

  • NLP_UNAVAILABLE

    The extraction service is not configured or unreachable.