Structured Note Reference Data

Explorer Contact Us
More ▾

Structured Note Conditions: API

We extract a structured note's terms from its offering document. The outcomes of those terms change with the market on any trading day: the underlier passes or breaches a coupon barrier, an autocall trigger fires, a floating rate fixes, or the note matures and settles. The conditions API returns the note's current condition state and the full ordered log of its condition events since issuance.

Condition data is computed. After the close on business days, the engine takes the note's observation schedule and barriers from its reference record, compares them with the day's underlier levels and rate fixings, and recomputes the note from inception. The data comes entirely from our calculation over the reference terms and market data.

The API base URL is https://api.sqx.com/v1/. Requests authenticate with an API key in the x-api-key header. Contact us for a key.

Like the pricing API, and unlike the reference-data API with its job handles and 202 responses, the conditions API is synchronous: the response contains the full result. A second read is useful after we repair a refused note (see "Repairing a refused note" below), and the first read in that case also returns 200.


Quick start

curl -H "x-api-key: $API_KEY" \
    "https://api.sqx.com/v1/conditions/byIsin?isin=US17332URD09"

The response is 200 with the note's condition state and its full event history, or 404 when the ISIN lacks condition state.


Successful response

All responses, successful or failed, use the v1 API envelope: a data object, an errors array (empty on success), and a meta block with a request ID and the server's as_of timestamp. On a 200, data contains four keys: the requested isin, a state object, an events array, and a reextraction block, which is null on almost all calls (see "Repairing a refused note").

This real response shows only the state block and the first event:

{
    "data": {
        "isin": "US17332URD09",
        "state": {
            "evaluable": "TRUE",
            "ineligible_reason": null,
            "status": "AUTOCALLED",
            "autocall_date": "2026-05-27",
            "last_observation_evaluated": "2026-05-27",
            "next_observation_date": null,
            "pending_data_observations": 0,
            "missed_coupons_in_memory": 0,
            "settlement_ineligible_reason": null,
            "last_evaluated_at": "2026-07-24 23:46:32",
            "changed_date": "2026-07-25 04:46:33"
        },
        "events": [
            {
                "observation_date": "2026-03-27",
                "event_type": "COUPON_EARNED",
                "payment_date": "2026-04-01",
                "threshold_pct": "70.0000",
                "worst_underlier": "Nasdaq-100 Index®",
                "observed_level": "23132.769531",
                "initial_level": "24960.040000",
                "performance_pct": "92.6792",
                "measurement_basis": "LEVEL",
                "coupon_rate_pct": "13.800000",
                "recovered_missed_coupons": null,
                "rate_code_1": null,
                "rate_value_1": null,
                "rate_code_2": null,
                "rate_value_2": null,
                "payment_pct_of_notional": null,
                "changed_date": "2026-07-24 01:36:10"
            },
            { "...": "three more events" }
        ],
        "reextraction": null
    },
    "errors": [],
    "meta": {
        "request_id": "3f1c8a2e-9b04-4e77-bd1a-0c2f5e6a7d10",
        "as_of": "2026-07-25T20:00:00+00:00",
        "page": null
    }
}

Both objects always contain the full key set for all notes. A field that is inapplicable to a note or event comes back as JSON null, the API's only placeholder value.

Decimal fields are JSON strings, for example "70.0000". The engine compares these decimal values with decimal thresholds, and converting them to floating point can change the result several decimal places out. Parse them with a decimal type. Integer counters (pending_data_observations, missed_coupons_in_memory, recovered_missed_coupons) are JSON numbers. Dates use YYYY-MM-DD; timestamps use YYYY-MM-DD HH:MM:SS in UTC.

The OpenAPI 3.1 spec (openapi.yaml) is the authoritative contract for all keys, enums, status codes, and the response envelope. This page is a narrative guide; the spec is the source of truth.

The state block

state is the note's current position. We rewrite it whenever we re-evaluate the note.

The two timestamps serve different purposes. To detect a transition, such as a decided coupon or a fired autocall, watch changed_date. We re-evaluate notes far more often than their results change, so last_evaluated_at would signal many recomputes with identical results. To detect that we have re-evaluated the note since a given time, watch last_evaluated_at. Use it when you wait for a repair (described below): a recompute that confirms the previous verdict is a valid result, and changed_date stays unchanged in that case.


The events log

events is a sparse log of transitions in order of observation_date, and then of event_type within a date. It records only decisions. A fixed unconditional coupon is certain and produces zero events, so a plain fixed-rate note can return an empty events array with a valid state.

The five event types:

event_typeMeaning
COUPON_EARNEDThe note met the contingent coupon condition on the observation date. On a memory note, recovered_missed_coupons gives the count of banked coupons released with the coupon.
COUPON_MISSEDThe underlier breached the coupon barrier, and the coupon is unpaid. On a memory note, the coupon is banked for later release.
AUTOCALLThe note met the autocall trigger and terminated early. Includes payment_pct_of_notional.
RATE_RESETThe engine determined a floating or steepener coupon rate for the period. Emitted for unconditional floaters. A contingent floater records the rate on its COUPON_EARNED or COUPON_MISSED row instead.
MATURITY_SETTLEMENTPrincipal redemption at maturity. Includes payment_pct_of_notional.

Event fields:

The API reports coupon rates and leaves the per-period coupon cash amount to the client. coupon_rate_pct is annualized, and day-count conventions are unreliable to extract from term sheets, so a cash figure would be a guess. Approximate the amount as coupon_rate_pct times the period fraction, using consecutive payment_date values for the period.


Example

US17332URD09 is a worst-of autocallable on the Nasdaq-100 and the Energy Select Sector SPDR Fund: a 13.8% contingent coupon observed monthly, a 70% coupon barrier and a 100% autocall trigger, without a memory feature. Its four events cover the full life of the note.

observation_dateevent_typethreshold_pctworst_underlierperformance_pctpayment_pct_of_notional
2026-03-27COUPON_EARNED70.0000Nasdaq-100 Index92.6792null
2026-04-27COUPON_EARNED70.0000Energy Select Sector SPDR Fund101.5200null
2026-05-27AUTOCALL100.0000Energy Select Sector SPDR Fund101.8866100.0000
2026-05-27COUPON_EARNED70.0000Energy Select Sector SPDR Fund101.8866null

The example shows four behaviors.

Performance below 100% can still earn the coupon. On March 27 the worst leg was down 7.3% from its initial fixing, and the note earned the coupon, because 92.68% is above the 70% barrier. Use event_type to determine the outcome, and treat performance_pct as the measurement only.

The worst leg can change. The Nasdaq-100 decided the March observation, and the Energy SPDR decided the next two. worst_underlier is a per-observation value, so it appears on the event rather than in the state block.

A date can pass two thresholds. On May 27 the worst leg was at 101.89% of initial, above both the 70% coupon barrier and the 100% autocall trigger. The two tests are independent and produce two events.

Redemption and coupon are separate events. May 27 has two rows: the AUTOCALL, with a payment_pct_of_notional of 100.0000 (par, because the note lacks a call premium), and the call-date coupon as a separate COUPON_EARNED. To total the cash flows, add both. payment_pct_of_notional is principal only, on autocall and at maturity alike.


Memory coupons

A memory note banks a missed coupon and releases the full backlog at the next passing observation. The API shows both steps: COUPON_MISSED rows accumulate, state.missed_coupons_in_memory gives the current backlog, and the COUPON_EARNED event that releases the backlog reports the count in recovered_missed_coupons.

US09711NES53 shows a full cycle against an 80% barrier: a miss at 75.75%, then a recovery at 85.22% with recovered_missed_coupons: 1 (two coupons paid on the date), then three more misses. The note is still live, with missed_coupons_in_memory at 3. The misses include a reading of 79.9910% against a threshold of 80.0000%, a shortfall of nine thousandths of a percent. The API returns decimals as strings to preserve differences of this size.


Maturity settlement

At maturity, we evaluate the note's piecewise payoff profile at the final observed performance and emit a MATURITY_SETTLEMENT event with the redemption in payment_pct_of_notional. We interpolate the profile linearly between recorded breakpoints and only within their range.

US48134XYY56 is a Russell 2000 note with a capped payoff. Its final performance was 143.7478%, and the note redeemed at the cap of 118.5000.

As with autocall, the figure is principal only. A final coupon appears as a separate COUPON_EARNED row.

When we are unable to establish the settlement precisely, we omit the MATURITY_SETTLEMENT event, and state.settlement_ineligible_reason gives the reason:

settlement_ineligible_reasonMeaning
no_piecewise_payoffThe extraction lacks a payoff profile for the note.
final_observation_pendingThe final closing level awaits ingestion. This is the only transient reason, and the engine retries it on all later runs.
final_level_unanchorableWe were unable to anchor the final level to the note's initial fixing.
final_level_outside_piecewise_rangeThe final level lies beyond the recorded breakpoints, and extrapolation would be a guess.
piecewise_unbounded_segmentThe profile ends in an unbounded "unlimited upside" segment whose slope is unrecoverable.
piecewise_ambiguous_at_levelThe final level falls precisely on a digital jump, or on a duplicated non-monotone branch.

A settlement refusal affects only the settlement. The API still returns the note's full coupon and autocall history.


Evaluate or refuse

The endpoint refuses a note whose conditions we are unable to evaluate precisely. A refused note has evaluable of "FALSE", a reason, and an empty events array. We report a refusal in preference to a coupon verdict computed from an underlier we identified with low confidence.

Reason tokens are stable strings for filtering:

ineligible_reasonMeaning
no_observation_scheduleThe extraction lacks an observation schedule.
unparseable_schedule_dateA schedule date has a zero day or month, and substituting a real date would produce a wrong evaluation.
unsupported_observation_scopeAn observation scope outside the engine's support. Supported scopes: a sole underlier, worst-of, and weighted-average baskets.
unresolved_underlierAn underlier we were unable to map to a price or level series.
missing_initial_fixingAn underlier lacks an initial fixing level.
initial_level_unverifiableWe have a level series, but it fails to reconcile with the printed initial fixing, so we treat the series as unreliable.
unknown_rate_indexA floating-rate benchmark outside our fixings coverage.
rate_threshold_direction_unrecordedA rate barrier whose direction (above or below) is missing from the extraction.
contingent_coupon_without_barrierThe coupon is contingent, but the extraction lacks the barrier for the test.
basket_leg_unresolvedWe were unable to anchor a basket leg's initial level.
basket_weights_incompleteFewer than two legs, a missing or non-positive weight, or weights whose sum is far from 100.
unsupported_basket_observation_typeA window-averaged rather than point-in-time closing basket observation.
unsupported_basket_calculation_methodA basket calculation method other than an explicit weighted average.

Refusals are temporary in most cases and clear automatically. When we validate an underlier mapping or backfill a missing level series, the engine re-selects the note on the next daily run and clears the refusal before its next observation date.


Repairing a refused note

Some refusals come from a gap in the extracted terms instead of a gap in market data. We extracted the note before the extractor could capture the field the verdict needs. We keep a manifest that maps notes to the extraction capabilities they lack.

A request for a refused note checks the manifest. If the ISIN is on the manifest, the call starts the re-extraction and reports the request in the reextraction block:

{
    "isin": "US0000000000",
    "state": {
        "evaluable": "FALSE",
        "ineligible_reason": "missing_initial_fixing: Nasdaq-100 Index®",
        "...": "..."
    },
    "events": [],
    "reextraction": {
        "tables": "r_note_underlying_link",
        "command_id": "8f3c0b1e-2a55-4c07-9d18-6b2e1f0a4c73"
    }
}

tables lists the term sections we are re-extracting from the filing. command_id is our internal identifier for the run, for reference when you contact us about the repair. To detect the result, use the method below.

Detecting the corrected verdict

The first read returns 200 and the current verdict immediately. The repair runs in the background, and when the re-extraction completes, we recompute the note at once, ahead of the overnight batch.

Re-read the endpoint and watch state.last_evaluated_at:

before=$(curl -s -H "x-api-key: $API_KEY" "$url" | jq -r .data.state.last_evaluated_at)
deadline=$(( $(date +%s) + 180 ))
while [ "$(date +%s)" -lt "$deadline" ]; do
    sleep 15
    now=$(curl -s -H "x-api-key: $API_KEY" "$url" | jq -r .data.state.last_evaluated_at)
    [ "$now" != "$before" ] && break      # recomputed — read the verdict
done

Analysis runs on a pipeline separate from the API, so frequent polling leaves its speed unchanged: poll at 15-second intervals. Set a time limit on the loop. If a failure occurs on our side, a loop that stops and reports the failure is more useful than an endless loop.

Re-extraction of a term section usually completes in under a minute; several sections take longer. If the repair is unable to run promptly, it moves to the overnight batch, and the next business-day read shows the corrected note.

reextraction returns null after the refusal is resolved.

Scope of the repair

The repair runs alongside a 200 response. The reference-data API withholds its payload behind a poll handle when a newer filing supersedes the record on file, because the old terms would be stale. A condition state is a current evaluate-or-refuse verdict, so the API returns 200 and the verdict immediately while the repair runs.

The repair can leave the verdict unchanged. A re-extraction supplies only terms that we can extract from the filing. If last_evaluated_at has advanced and the refusal remains, the filing lacks the term or our extractor is still unable to capture the term. Further reads will show the refusal until our extraction improves. Refusals that depend on market data, such as initial_level_unverifiable, require a price, which re-extraction is unable to supply.

The repair runs only for notes with an unresolved refusal. Candidates have an ineligible_reason, or a settlement refusal other than the transient final_observation_pending (a re-extraction is unable to supply a missing market close). reextraction is null on an evaluable note, even if the manifest lists the note for a capability, and on a note whose repair requires a full re-analysis rather than a targeted re-extraction.

The block is best-effort: if we are unable to dispatch the repair, the read still returns 200 with reextraction as null. A conditions read succeeds regardless of background repair work.


Update schedule

The engine runs after the close on business days, after the day's prices load. The nightly run produces almost all changes. The exception is a repair recompute, which updates only the note whose terms we just re-extracted. Watch state.changed_date and the changed_date on events; they change only when the data changes.

Plan for two consequences.

Events can change. We recompute a selected note from inception, so a revised or corrected market print can change a past event's performance_pct, reverse a verdict, or add an event where a pending observation was. changed_date shows these revisions. If you cache results, key the cache on changed_date instead of the fetch time.

Conditions and the reference record are separate endpoints. Reference terms from /reference/byIsin change rarely and can be cached for a long time, while condition state changes daily. Combining them would make the terms uncacheable. Fetch terms rarely and conditions often.


Coverage

We compute conditions from the reference record, so a note requires extracted terms before it can have condition state. The coverage below is the upper limit for this endpoint and for /reference/byIsin. A note in the "On request" column needs an extraction first (request the ISIN from /reference/byIsin) before the nightly engine includes the note.

To check a list of ISINs free of charge, POST the list to /reference/coverage.

What we cover, by issuer

Structured notes in our security master, and the response to a request for a note.

Terms ready: we have extracted the payoff curve, underliers and coupon mechanics, and /reference/byIsin returns the terms immediately.

On request: we have identified the offering document and extract the document on request. A request for the ISIN starts the extraction and returns a poll URL; the terms arrive a few minutes later. Advance arrangement is unnecessary.

The remainder are securities without an offering document. They return reference_scope: security_master_only, and that response is free: it leaves your distinct-ISIN limit unchanged. To check a list before requesting the ISINs, POST the list to /v1/reference/coverage, also free.

IssuerNotes on fileTerms readyOn requestReachable
JPM80,3083,72549,34666.1%
GS38,5742,99827,15478.2%
MS40,3832,66729,74680.3%
Barclays29,4722,49820,44777.9%
Citi50,9882,22529,29461.8%
UBS39,6622,21134,84693.4%
BOFA23,9221,89116,40276.5%
BMO16,5141,30212,67184.6%
HSBC17,3337649,06056.7%
BNS13,1706413,60832.3%
RBC12,2176309,51283.0%
TD12,29062110,19388.0%
BNP51,31414315,27130.0%
SG10,008101071.2%
BBVA14,787220.0%
CS11,16906,57058.8%

Issuers with fewer than 5,000 notes are omitted. Coverage depends on offering documents: we extract terms from the offering document, so an issuer whose US notes are unregistered with the SEC shows low coverage here. A distributor feed is the only route to those terms, and work on our SEC pipeline leaves the figure unchanged.

Counts as of 2026-08-14.


Input errors

Invalid input returns 400 with a structured error in the errors array, in the ErrorDetailV2 format used across the v1 API: a numeric messageCode, a category, a machine-readable type, and a human-readable message:

{
    "data": null,
    "errors": [
        {
            "category": "VALIDATION_ERROR",
            "type": "INVALID_INPUT",
            "messageCode": 1010,
            "message": "Invalid ISIN: checksum invalid",
            "messageValues": { "isin": "US06370EDU90" }
        }
    ],
    "meta": { "...": "..." }
}

Filter on messageCode. The codes are stable, and the message wording can change. Platform-wide codes:

messageCodeWhen
1001A required parameter is missing. messageValues.parameter identifies the parameter.
1010The ISIN is malformed or fails its check digit; also used for a filter that the route rejects.
1011CUSIP is malformed or fails its check digit.
1012A date is an invalid YYYY-MM-DD value, or a window's to is before its from.
1015The request refers to an item outside the route's offering (a field, a product, or a job kind), or requests intraday prices for a security other than a treasury. messageValues identifies the item.
1033The API key's grant has lapsed (403).
1040The identifier is well-formed, but the security or job is absent from our data (404).
1050The key is over a distinct-ISIN quota (lifetime or monthly), over its per-second rate, or a bulk request exceeded its cap (429).
1060A database failure on our side, or an asynchronous job that failed or was unable to start (500 / 503).

The API converts lowercase identifiers to uppercase, so identifiers pasted from a spreadsheet work as entered.

The table above covers the full platform. This endpoint accepts only an ISIN, so its input errors are limited to 1010 and 1040; 1011 and 1012 apply to CUSIP and date parameters, which this endpoint lacks.

Two responses have a narrower meaning here than on the other endpoints.

A 404 means the ISIN lacks condition state: the security is outside our condition tracking, or the daily engine has yet to process the note. The security can still exist in our data elsewhere: an ISIN with full reference and pricing coverage can return 404 here.

A refused note returns 200, a full state with evaluable of "FALSE" and a reason, and an empty events array.


Endpoint reference

MethodPathPurpose
GET/conditions/byIsin?isin={ISIN}Condition state and full event history for a structured note. 200 with state and events, or 404 when the ISIN lacks condition state.
GET/versionReport the API version. Open without authentication.

You can also browse the payload for a covered ISIN on this site, at https://sqxnotes.com/api/conditions/US17332URD09.json. Its isin, state, and events keys match the live API's data payload. The static file also contains timeline-overlay keys for the Explorer's charts (schedule, underliers, composite, scope, maturity_date, as_of). These keys are site rendering data and fall outside the API contract.


Authentication & versioning

All requests except /version require a valid API key in the x-api-key header. Keys are scoped per client. A key can also have an expiry, a lifetime distinct-ISIN quota (a cap on the total distinct ISINs the key can retrieve), a monthly distinct-ISIN cap per product or across a chosen set of products, and a per-second request rate. A key whose grant has lapsed receives 403 (messageCode 1033). A key over a quota, a monthly cap, or its rate receives 429 (messageCode 1050, with the limit and window in messageValues).

The v1 base path is stable. Field additions are non-breaking and ship in place. Breaking changes (renames, removals, and changes of meaning) ship as a new major version on a new stage. /version reports minor and patch updates.

Two details apply to this endpoint. Access comes through the structured-note reference scope: a key entitled to reference.structured_note, or to the broader reference, can call the endpoint. Metering is separate: conditions bill on their own ledger, independent of reference and pricing, so an ISIN counts separately for conditions, reference, and pricing.

The event-type and reason enums above are open. New tokens can appear within a major version, so treat an unrecognized reason token as a refusal rather than a parse error.

Want this data flowing into your systems? Talk to us about coverage, delivery, and the API.

Contact us to learn more