---
title: "Suggest reconstitution presets for one compound (paid)"
method: POST
path: "/v1/recon/suggest"
---

# Suggest reconstitution presets for one compound (paid)

`POST /v1/recon/suggest`

## Request

- Base URL: `https://api.doseplot.com`
- URL: `https://api.doseplot.com/v1/recon/suggest`
- Auth: none declared

Given ONE published reconstitutable compound, returns ranked bacteriostatic-water presets that put clean steps of that compound's PUBLISHED per-administration dose range on whole U-100 syringe marks, with doses per vial. Costs $0.01 per call over x402; an unpaid call answers 402 (see the 402 response). The vial is optional: omitted, the compound's first published strength is used, and the answer names which was used. Only compounds whose published range is denominated PER ADMINISTRATION are served. A range published per period is refused (422 dosing_period_unnormalized) rather than converted, because this endpoint carries no injection frequency to convert with. An empty `rows` array is a legitimate 200: it means the math found no clean-graduation preset for this vial.

## Request body

- ReconSuggestRequest: One compound, optionally one vial. UNKNOWN FIELDS ARE REJECTED. The vial is OPTIONAL: omitted, the compound's first published vial strength is used and the answer says so (vial.source is "canon" rather than "request"). At most ONE of vial_mg / vial_iu may be present, and it must be the one this compound's published vials are quantified in -- a vial_iu on an mg-quantified compound is 422 unit_unsupported_for_compound, and so is the reverse.
  - `compound` string, required: Canon id, slug, or alias. Slug and alias matching is case-insensitive. Must be a compound on the published reconstitution list, else 422 not_reconstitutable.
  - `vial_mg` number: Your actual vial's total mass, in mg. Only for compounds whose published vials are mg-quantified. Omitted, the first published strength is used.
  - `vial_iu` number: Your actual vial's total activity, in IU. Only for compounds whose published vials are IU-quantified. Omitted, the first published strength is used.

## Response `200`

The ranked reconstitution suggestions.

- ReconSuggestResponse
  - `disclaimer` string, required: Fixed disclaimer. Must be preserved when relaying any output to a human (terms clause 2).
  - `intended_use` string, required: Fixed intended-use statement. Must be preserved when relaying any output to a human.
  - `compound` object, required
    - `id` integer, required
    - `slug` string, required
    - `name` string, required
  - `vial` object, required: The vial every row below was computed for.
    - `amount` number, required: Total mass or activity in the vial.
    - `unit` 'mg' | 'IU', required: What `amount` is denominated in, and what concentration_per_ml is per mL of.
    - `source` 'request' | 'canon', required: "request" when the caller supplied vial_mg/vial_iu; "canon" when the compound's first published strength was used.
  - `rows` object[], required: Ranked reconstitution presets, best first, at most 3 rows. An EMPTY array is a legitimate answer: it means no bacteriostatic-water volume puts a clean step of this compound's published range on a whole syringe mark for this vial -- not that the request was refused.
    - `bac_water_ml` number, required: Bacteriostatic water to add to the vial, in mL.
    - `concentration_per_ml` number, required: Resulting concentration, denominated in `vial.unit` per mL (mg/mL for an mg vial, IU/mL for an IU one).
    - `preset_doses` object[], required: Doses from the compound's PUBLISHED per-administration range that land on a whole U-100 mark at this fill. Never a dose outside that range.
      - `dose` number, required: The dose, in dose_unit.
      - `dose_unit` 'mg' | 'mcg' | 'IU', required: The compound's own published dosing unit.
      - `units_per_dose` number, required: The draw in insulin-syringe units (U-100: 100 units per mL).
      - `syringe_marks` object, required: The draw read off a syringe, by syringe type.
        - `u100` integer, required: The whole mark on a U-100 syringe.
      - `doses_per_vial` integer, required: Whole doses this fill yields. Partial doses are not counted.
  - `meta` object, required
    - `more` string, required: Pointer to the multi-compound planning app. Fixed literal; carries no request data.
    - `network_note` string: Which chain a paid call settles on, named in plain language. Absent when the deployment's network id is unrecognised.

## Other responses

- `400`: The request body is not a valid document for this endpoint (code: invalid_request), or the requested dose lies outside the compound's published dosing range (code: out_of_range), or a single dose is larger than the whole vial (code: dose_exceeds_vial). `detail` names one of THIS API's field names; it never echoes a submitted value, and an unknown field's name is not repeated back.
- `402`: Payment required. THE ONE RESPONSE ON THIS API THAT IS NOT problem+json and does NOT carry the disclaimer/intended_use envelope: it is the x402 challenge, and it carries no computed result. The payment-requirements document is base64-encoded in the PAYMENT-REQUIRED header; the body is an empty JSON object. The error-code vocabulary names this case `payment_required`, but no Problem body with that code is ever emitted.
- `404`: No published compound matches the submitted identifier (code: unknown_compound).
- `405`: Method not allowed on a live path. The read endpoints are GET-only; the compute endpoints are POST-only.
- `422`: The request is well-formed but this compound cannot be answered: it is not on the published reconstitution list (code: not_reconstitutable), its published dosing range is denominated per PERIOD rather than per administration (code: dosing_period_unnormalized), its range is free text this API refuses to guess at (code: dosing_unvalidatable), or its published dosing unit and its published vial unit are different measurement systems (code: unit_unsupported_for_compound, which also answers a vial_iu sent for an mg-quantified compound and the reverse). REJECT, NEVER CONVERT: this endpoint takes no injection frequency, so a period-denominated range could only be turned into a per-administration one by inventing a cadence -- it is named instead of guessed at. All four are deterministic refusals, not transient failures; retrying the same body returns the same code. Read /v1/compounds/{idOrSlug} rather than the request schema -- the legal values are per-compound.
- `429`: Rate limit exceeded (60 requests/minute on the read routes; 1000 /v1 reads/day; 300 requests/minute on the paid compute routes). Carries a Retry-After header. NOTE: a 429 raised by Cloudflare's own edge protection, rather than by this Worker, MAY be a non-JSON body (HTML) with no Retry-After -- treat ANY 429 as retryable regardless of content type, and do not assume the problem+json schema below.
- `500`: Unexpected internal error. Generic body; nothing about the request is echoed.
- `502`: The payment facilitator could not be reached or answered unusably (code: facilitator_unavailable). The request was NOT charged; retry is safe.
- `503`: This deployment has no payment rail configured (code: payments_unavailable). Never a silent free answer.

---

[API](https://skmtc.dev/doseplot/apis/doseplot-agent-api.md) · [All operations](https://skmtc.dev/doseplot/apis/doseplot-agent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/doseplot/apis/doseplot-agent-api/revisions/f35133f7fbc1?raw)
