GET /medication/resolve
Resolves a medication identifier into its official identity card. On the French side, from the BDPM register (ANSM/HAS): marketing-authorization status, active substances normalized to the INN, presentations with their public price and reimbursement rate, and generic group. The same endpoint resolves a US medication by NDC or name from openFDA + RxNorm. The answer is served from local stores built from those official datasets, so a lookup resolves in milliseconds with no network call and no secret at request time.
Use it when an agent needs a grounded, sourced answer before acting on a
prescription or an order - “what is behind this CIS code on a prescription,
and is its authorization still active?”, “what is the price and reimbursement
rate of this box?”, “what active substance does this US NDC contain?”. The
medical vertical on x402 is otherwise a crowd of near-identical openFDA
wrappers with no French or EU coverage at all; here it is one paid call,
no account, over the official French register. Lookup is by identifier or
exact name only - there is no search by symptom, indication or fuzzy
name in v1; the endpoint serves agents that already hold an identifier.
See the live /catalog for the authoritative endpoint
listing and price.
This endpoint returns sourced, dated facts - authorization labels, prices and rates exactly as published. It is never clinical advice, a patient dosage, or a risk score. That line is the boundary of the service.
x402 golden rule: the agent pays for the answer to its question. A
well-formed identifier that the register does not carry is a successful
answer too -> 200 billed with data.found: false - the absence from the
register is the information. Only requests the service cannot answer - no
key, several keys, a malformed key, a name without a country - leave the 200
range and are not billed.
Parameters
Exactly one of the four keys, as a query parameter. Zero or several keys,
a malformed key, or country without name is a 400 INVALID_INPUT.
| Parameter | Type | Required | Description |
|---|---|---|---|
cis | string | one of the four | French CIS code - exactly 8 digits (BDPM) |
ndc | string | one of the four | US product NDC labeler-product (e.g. 0071-0155); a package NDC (3 segments) is malformed |
name | string | one of the four | Exact product name - requires country=FR|US |
dci | string | one of the four | INN/DCI substance name - pure local computation, no store |
country | string | with name only | FR or US - selects the single register the name is resolved against |
GET /medication/resolve?cis=60424338
A name without a country is a 400 MISSING_PARAMETER: a bare name would
resolve across both registers and break the single-source provenance, so the
caller must say which register to query. Name resolution folds case, accents
and punctuation and may return several medications.
200 response - UnifiedResponse
{
"data": { ... },
"provenance": {
"source": "BDPM",
"fetched_at": "2026-07-19T18:10:33Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-18T00:00:00Z" }
}
}
provenance depends on the key that resolved the answer:
| Key(s) | source | freshness.kind |
|---|---|---|
cis, name + country=FR | BDPM | snapshot - monthly BDPM image |
ndc, name + country=US | openFDA + RxNorm | snapshot - openFDA dump date |
dci | medication-grid DCI/INN substance table | live - pure local computation |
Fields of data
| Field | Type | Description |
|---|---|---|
found | bool | false alone when the key is well-formed but unknown to the register - a billed 200 |
medications | object[] | Present for cis / ndc / name - one entry per matched medication (see below) |
substance | object | Present for dci only - the canonical substance, present only when the DCI mapped to the INN table |
coverage | object | Present only when some substance label could not be normalized (see below) |
A field the source does not carry is omitted from the JSON, never served
null: a US result carries no status or presentations, a French result
carries no generic_name.
A medications[] entry
| Field | Type | Description |
|---|---|---|
country | string | FR or US |
identifiers | object | { cis } (FR) or { ndc } (US) |
name | string | Product name (brand) |
generic_name | string | US only - openFDA generic name when distinct from the brand name |
form | string | Pharmaceutical form |
routes | string[] | Routes of administration |
holder | string | Marketing-authorization holder (FR) / labeler (US) |
status | string | FR only - marketing-authorization status, as published |
marketing_status | string | FR only - marketing status |
authorization_date | string | FR only - authorization date (YYYY-MM-DD) |
enhanced_surveillance | bool | FR only - ANSM black-triangle enhanced-surveillance flag |
dispensing | string[] | Dispensing conditions as published (FR); prescription / otc derived from product_type (US) |
substances | object[] | Active substances normalized to the INN - the FR↔US bridge (see below) |
presentations | object[] | FR only - boxes with price and reimbursement rate (see below) |
generic_groups | object[] | FR only - { id, label } of the generic group only - the members belong to a dedicated /medication/generics endpoint (not documented here) |
A presentations[] entry (FR only)
| Field | Type | Description |
|---|---|---|
cip7 / cip13 | string | CIP presentation codes |
label | string | Packaging label |
price_cents | int | Public price in euro cents (integer, never a monetary float); omitted when the price is free/unrecorded |
reimbursement_rate | string | UNCAM rate as published (e.g. "65%"); omitted when not reimbursed/unrecorded |
collectivities_approved | bool | Approved for use by public bodies |
Prices and rates are relayed exactly as published - they are facts, not a
recommendation. The generic-group label is the group name only; its members
are the job of a dedicated /medication/generics endpoint.
The FR↔US bridge is the substance
Every active substance in a medications[] entry is normalized to its INN,
and a French cis and a US ndc for the same molecule carry the same
substances[].id. That normalized id is the join key: it is what will let a US
medication be looked up against the French ANSM interaction thesaurus. The
paracetamol of a French DOLIPRANE and a US DOLOCOMBO both resolve to
id: "paracetamol".
Coverage honesty
Some published substance labels do not match an entry in the DCI↔INN table. The
service never guesses them: it relays the published label as-is in
substances[] with mapped: false, and lists it in
coverage.unmapped_substances. When every substance normalized, coverage is
absent. The service relays the source’s label; it does not invent one.
Example - French medication, resolved by CIS
GET /medication/resolve?cis=60424338 (AMOXICILLINE VIATRIS), against the
2026-07-18 BDPM snapshot:
{
"data": {
"found": true,
"medications": [
{
"country": "FR",
"identifiers": { "cis": "60424338" },
"name": "AMOXICILLINE VIATRIS 500 mg, gélule",
"form": "gélule",
"routes": ["orale"],
"holder": "VIATRIS SANTE",
"status": "Autorisation active",
"marketing_status": "Commercialisée",
"authorization_date": "1998-12-10",
"enhanced_surveillance": false,
"substances": [
{
"id": "amoxicilline trihydratee",
"labels_fr": [],
"labels_en": [],
"raw_label": "AMOXICILLINE TRIHYDRATÉE",
"source_code": "28165",
"mapped": false
}
],
"presentations": [
{
"cip7": "3491317",
"cip13": "3400934913174",
"label": "plaquette(s) thermoformée(s) PVC-aluminium PVDC de 12 gélule(s)",
"marketing": "Déclaration de commercialisation",
"declared_at": "2014-07-04",
"collectivities_approved": true,
"reimbursement_rate": "65%",
"price_cents": 153
}
],
"generic_groups": [
{ "id": 148, "label": "AMOXICILLINE 500 mg - CLAMOXYL 500 mg, gélule" }
]
}
],
"coverage": {
"unmapped_substances": ["AMOXICILLINE TRIHYDRATÉE"]
}
},
"provenance": {
"source": "BDPM",
"fetched_at": "2026-07-19T18:10:33Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-18T00:00:00Z" }
}
}
Example - US medication, resolved by NDC
GET /medication/resolve?ndc=0071-0155 resolves against the openFDA/RxNorm
store. The entry carries country: "US", identifiers.ndc, name (brand) and
generic_name, dispensing: ["prescription"], and substances[] whose id
values are the same as on the French side - the bridge. It carries no
status, presentations or generic_groups (French only), and
provenance.source is "openFDA + RxNorm". Note there is no US price: the
price and reimbursement fields are French only.
Example - substance, resolved by DCI
GET /medication/resolve?dci=paracetamol returns data.substance (the
canonical substance) with no medications, provenance.source: "medication-grid DCI/INN substance table" and freshness.kind: "live" - it is
a pure local computation over the committed DCI↔INN table, consulting no store
(so a dci key never returns a 503). A DCI outside the table is a billed
200 with data.found: false, never a guess.
A well-formed but unknown key is a paid answer
GET /medication/resolve?cis=99999999 - a well-formed CIS the register does
not carry - returns a 200, billed, with data.found: false alone. The
agent asked “is this identifier a real medication?”, and “the register does not
carry it” is the answer. Contrast this with the 400s below, which are
malformed requests and are not billed.
Freshness
French answers are served from a monthly snapshot of the BDPM register, US
answers from the dated openFDA dump; freshness.as_of carries the date of
the image that backed the answer. If a monthly refresh is missed, the service
keeps serving the last good snapshot - honestly dated by as_of - and never
turns staleness into a 5xx. A register not yet ingested at all is a
503 DATA_UNAVAILABLE (there is no last good snapshot to serve); a dci key
depends on no store and never returns 503.
Errors
Only requests the service cannot answer leave the 200 range - none of them are billed.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_INPUT | Zero or several keys; a malformed key (e.g. a cis that is not 8 digits, a package NDC); country without name; an invalid country |
| 400 | MISSING_PARAMETER | name provided without country |
| 503 | DATA_UNAVAILABLE | The required register has not been ingested yet - there is no last good snapshot to serve |
| 404 | NOT_FOUND | Unknown route |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "provide exactly one of: cis, ndc, name, dci", "code": "INVALID_INPUT" }
{ "error": "parameter `name` requires `country` (FR or US)", "code": "MISSING_PARAMETER" }
Attribution
French medication data is derived from the BDPM (Base de données publique des médicaments), made freely available under article L.161-40-1 of the French Social Security Code (source and date required). US medication data is derived from openFDA and RxNorm, in the public domain (NLM/FDA).
See also
GET /company/resolve- resolve a French company from its SIREN or SIRET before onboarding a counterparty.GET /iban/screen- screen a counterparty’s IBAN against the OFAC/EU/UN sanctions lists before paying it.- For agents - discovery surfaces, the live
/catalogand how settlement works.