# Patternode for developers > Integrate with Patternode. Build and test against the mock (mock/mock_patternode.py) first; the specs at /specs/ are the contract; never put an API key in code, a file, a log or a chat. This file contains all documentation content in a single document following the llmstxt.org standard. ## Create a key The response is the only time the key is shown. Store it in your secret store before doing anything else. Expiry is fixed at creation. An account holds at most five active keys of each kind. Request --- ## Segment a large file from your storage, as a job For bodies over 5 MB and backfills. The file stays in your storage: give a link Patternode can read it from (`source_url`, a presigned GET) and a link it writes the result to (`result_url`, a presigned PUT). Patternode reads, segments, writes, and keeps nothing. Answers 202 at once; poll the job, or receive `segmentation.job.succeeded` or `segmentation.job.failed` at your webhook. The job record holds only its status and usage. A failed job is not charged. Request --- ## Delete a key (permanent) A key cannot delete itself. Use another admin key. Request --- ## Disable a key (reversible) Disable a key (reversible) Request --- ## Enable a disabled key Enable a disabled key Request --- ## Extend many forming series in one request Up to 1,000 items, each an `extend` of one series with its own edge and bars, answered in the same position. One item failing does not fail the others: check each result's `status`. Patternode keeps nothing between calls. Request --- ## Extend a forming series with new bars, from its edge Patternode keeps nothing between calls. A forming series is carried by you: each answer returns its `edge`, a sealed token holding the open right edge of every level. Store it with the segments, and send it back with the next bars. Without an `edge`, the call starts a new series. The same `edge` with the same bars always gives the same answer, so a retry after a timeout is safe: resend the request you sent. Bars must open after the edge's last bar closed (`edge_last_close_time`); an earlier bar is refused with 409 `out_of_order`. Extending bar by bar gives the same segments as sending them all as a finished series. The edge is sealed: encrypted and signed by Patternode, bound to your account, the series name and the profile version. You cannot read or change it, and it is no use to anyone else. What it means for drawing is returned beside it, readable, as `right_edge`. Request --- ## Balance, tier, limits and webhook URL Balance, tier, limits and webhook URL --- ## A job's status, and its result link when done A job's status, and its result link when done Request --- ## Your service level indicators, as Patternode measures them Your service level indicators, as Patternode measures them Request --- ## Usage by key and kind, hourly or daily Usage by key and kind, hourly or daily Request --- ## OAuth 2.0 client credentials with private_key_jwt (Mission Critical, Dedicated) RFC 6749 client credentials, the client authenticated by a JWT it signs (RFC 7523) with a key registered to it. Optionally bound to a client certificate (RFC 8705). The access token is valid for 15 minutes and is sent as a Bearer token like a key. Request --- ## Every change to the account and its keys, and every support access Every change to the account and its keys, and every support access Request --- ## List the account's keys List the account's keys --- ## Patternode API Patternode keeps none of your series: no copies, no checkpoints, no results. A forming series' state travels with you as a sealed `edge`; files are read from and written to your own storage. Only account, usage and audit metadata are kept. One API for every Patternode product, and one functional contract for every plan: a plan changes capacity, service levels and transports, never routes, fields or semantics. The service levels are the developer site's Service contract section. Resources are product-neutral; the tag on each operation names the product it belongs to. Delve is the first. Every operation carries `x-status`: - `available`: supported; changes follow the versioning policy. - `preview`: works, may change with 30 days' notice; not for production alarms. - `planned`: specified so you can design against it; returns 404 today. Conventions that apply to every route are in the Concepts pages of the developer site: authentication, the body hash on writes, idempotency, errors, rate-limit headers and versioning. **HTTP: Bearer Auth** `Authorization: Bearer pnk_live_...`, one space after Bearer. Security Scheme Type: | http HTTP Authorization Scheme: | bearer **API Key: apiKey** The same key in `x-api-key`, for clients that cannot set Authorization. Security Scheme Type: | apiKey Header parameter name: | x-api-key Contact Patternode partner support: [partners@patternode.com](mailto:partners@patternode.com) URL: [https://patternode.com/partners](https://patternode.com/partners) License Apache 2.0 --- ## Issue a new webhook signing secret Shown once. Deliveries are signed with the new and the previous secret until the next rotation, so a receiver can switch without dropping a delivery. Request --- ## Account --- ## AccountUpdate --- ## Bar One interval of a series: [open_time, close_time, open, high, low, close]. Times are integers since the Unix epoch, UTC, in the request's time_unit (ms by default); us and ns times are strings of digits in JSON. close_time >= open_time, high >= low, every value finite and above zero. Bars of a series must not overlap and must be in time order. Irregular spacing is fine: leave a gap where nothing happened rather than sending a flat bar. --- ## CreatedKey --- ## Edge A forming series' state, sealed by Patternode: encrypted, signed, and bound to your account, the series name and the profile version. Opaque: store it as you got it and send it back unchanged. Omit it to start a series. --- ## Error Every error from /v1/ has this shape. Branch on error.details.error_code, never on the message. --- ## ExtendRequest --- ## Extension --- ## Job --- ## JobRequest --- ## Key --- ## KeyIdentity --- ## KeyRequest --- ## Profile The segmentation profile, optionally pinned to a version: `standard` or `standard@2026-11`. Unpinned, the version current at the call (or, with an edge, the one sealed in it) is used, and the answer names it. Versions never change and are served for at least 24 months. Your onboarding pack names the profiles for your account. --- ## RunUsage --- ## Sample One reading: [time, value]. Time since the Unix epoch, UTC, in the request's time_unit, strictly increasing within a series. Value finite and above zero. --- ## Scope --- ## Segmentation --- ## SegmentationRequest --- ## SegmentBox One segment of a series' tree, as a partner receives it: its place in the tree and its four vertices (open, high, low, close, each with its time). Draw it as a box from open_time to close_time between low and high, with its line from open through high and low (in time order) to close. Nothing else of the tree leaves Patternode. --- ## SeriesName A name you make up for the series. Patternode stores nothing else about it, so make it opaque: a hash of your client and asset, not their names. --- ## TimeUnit The unit of every time in the request and the answer, since the Unix epoch, UTC. In JSON, `us` and `ns` times are strings of digits; `ms` times are integers. --- ## UsageReport --- ## Segment a finished series in one call Send a finished series as bars (or samples, which the service turns into bars). Back come the segment boxes at every level, parents before children, with the open right edge marked `provisional`. For more than 5 MB, run a job from your own storage. Patternode keeps nothing of the series after it answers. A test key accepts only `{"demo": "walk"}` or `{"demo": "sensor"}` and is never billed. Request --- ## Set the spend limit, auto-reload or webhook URL Set the spend limit, auto-reload or webhook URL Request --- ## The key and its account The key and its account --- ## Decisions you make For solution architects. Each decision has a default that suits most integrations; choose otherwise only for the reason given. Record your choices in your integration brief ([playbook, stage 0](../playbooks/delve-onboarding.md#stage-0-choose-the-grade-and-plan)). | # | Decision | Options | Default | Choose otherwise when | |---|---|---|---|---| | D1 | Plan | Evaluation, Production, Mission Critical, Dedicated | Production | Real-time operations or live trading depend on it (Mission Critical), or regulation needs single tenancy or audit rights (Dedicated) | | D2 | Jurisdiction | Australia and New Zealand, EU, US | Where your clients' data must stay | Never by latency alone: residency first | | D3 | Integration styles | Finished, forming, backfill, events | Backfill once, then forming, plus events | Reports that redraw daily need only finished series | | D4 | Extension transport | REST, batched REST, gRPC | REST | Many series at once (batched REST), or sustained high rates with tight tail latency (gRPC, Mission Critical) | | D5 | Event channel | Webhook, event bus, polling | Webhook | No inbound endpoint allowed (event bus on Mission Critical, or polling jobs) | | D6 | Authentication | API keys, OAuth with `private_key_jwt`, mutual TLS, workload identity | API keys | Policy forbids long-lived secrets (OAuth or workload identity), or requires client certificates (mutual TLS) | | D7 | Connectivity | Internet, private connectivity, dedicated interconnect | Internet with TLS | Traffic must stay off the internet (private connectivity, Mission Critical) | | D8 | Bar width | Seconds to a day | What your chart's finest view needs to read | Fewer, wider bars cut cost and rate; narrower ones show more at the finest level | | D9 | Profile version | Unpinned, pinned | Pinned in production | Never unpinned where results must reproduce (audits, backtests, models) | | D10 | Segment and edge storage | Object store, time-series database, relational database | One record per series holding segments and edge, by client prefix, with a version for one writer | You query segments across series (a database) | | D11 | Your own resilience | One region, two regions | One region on Production; two on Mission Critical | Your own service level is higher than one region gives | | D12 | Series naming | Hash of client and asset, with a salt | `hash(client)/hash(asset)/measure` | Even the kind of measure is sensitive: drop the suffix | [Detailed flows](flows.md) shows how each choice behaves at run time; [Plans](../service/plans.md) shows what each plan provides. --- ## Deployment and resilience For solution architects and the engineers who run the integration. [![Your callers in one or two cloud regions reach the jurisdiction endpoint over private link or the internet; Patternode runs active-active in two regions of the jurisdiction and keeps no series state, so either region answers any call](./img/deployment.svg)](./img/deployment.svg) ### What runs where | Layer | Production plan | Mission Critical and Dedicated | |---|---|---| | Jurisdiction | One, chosen at onboarding: `anz`, `eu` or `us` | The same | | Patternode regions | One region, three zones; a second region of the jurisdiction on standby | Two regions, active-active, each with three zones, holding no series state | | Isolation | Shared cells | Shared cells (Mission Critical) or cells for you alone (Dedicated) | | Endpoint | `{jurisdiction}.api.patternode.com`, or `api.patternode.com` for your home jurisdiction | The same, plus a private endpoint | | Your callers | Anywhere with outbound HTTPS | In the same cloud regions as the jurisdiction, ideally two, connected privately | ### Placing your side | Decision | Recommendation | |---|---| | Cloud region for your ingest workload | The same cloud and region family as your jurisdiction, so the network share of your latency budget is small and steady | | Number of regions | One for Production. Two for Mission Critical, active-active or warm standby, with your segment store and series registry replicated | | Connectivity | Internet with TLS for Production. AWS PrivateLink, Azure Private Link or Google Private Service Connect for Mission Critical, and the only route if you ask | | Endpoint | Always the jurisdiction endpoint, never an address you resolved once: failover moves it | | Connections | HTTP/2, kept open and reused; one gRPC connection carries many series | ### When something fails | Failure | You see | Your client must | |---|---|---| | A Patternode zone | Nothing | Nothing | | A Patternode region (Mission Critical) | Up to 60 seconds of 503s or resets, then normal service from the other region | Resend unanswered requests unchanged; reconnect gRPC; deduplicate events | | A region degraded but up | A drain: the same as a failover, without errors | The same | | Patternode overloaded or your plan's rate exceeded | 429 or 503 with `retry-after` | Wait as told, then resend unchanged | | Your region | Whatever your design allows | Fail over your own callers; carry each series on from the edge in your replicated store | | The network between you | Timeouts | Retry; consider private connectivity | Patternode holds none of your series, so none is lost in any of these: your edges are in your store. Recovery objectives by plan are in [Resilience](../service/resilience.md#recovery-objectives). ### Change and maintenance Mission Critical and Dedicated customers set blackout periods (trading hours, emergency declarations) in which nothing changes in their cells. Releases go out a cell at a time and roll back automatically on regression. A quarterly regional failover test is run in production and its results shared. Next: [Security and data](security.md). --- ## Detailed flows For engineers building the integration. Each flow names every call and what your code does when something goes wrong. The contract for each call is in the [API reference](/api/patternode-api); the events are in [Events](../events/index.md). ### Finished series ```mermaid sequenceDiagram autonumber participant C as Collector and bar builder participant I as Ingest workload participant K as Key vault participant P as Patternode API participant S as Segment store I->>K: read live key (at start, cached) C->>I: bars for one series window I->>P: POST /v1/segmentations {series, profile, bars}
Authorization, x-amz-content-sha256, Idempotency-Key alt 200 P-->>I: segments (every level), profile_version I->>S: upsert by id; replace provisional, keep final else 429 or 503 P-->>I: retry-after I->>P: same request, same Idempotency-Key, after the wait else 400 invalid_series P-->>I: problems [index, problem] I->>C: fix the named bars; do not retry unchanged end ``` ### Forming series: the edge travels with you Patternode keeps nothing between calls. The series' state is its edge, which lives in your segment store and is sent with each new bar. ```mermaid sequenceDiagram autonumber participant I as Ingest workload participant S as Segment store participant P as Patternode (any region) I->>P: POST /v1/segmentations/extend {series, profile, bars} (no edge) P-->>I: closed, right_edge, edge, edge_last_close_time I->>S: one write: closed (final), right edge, edge, version 1 loop each bar as it closes I->>S: read edge (version n) I->>P: POST /v1/segmentations/extend {series, edge, bars} P-->>I: closed, right_edge, a new edge I->>S: one write, if still version n: closed, right edge, edge, version n+1 end Note over P: a region fails; the endpoint moves within 60 s I-xP: extend (timeout or 503) I->>P: the same request, unchanged (retry with backoff) P-->>I: the same answer the first attempt would have given ``` Rules your code keeps: | Situation | Answer | Do | |---|---|---| | A retried request | The same answer, whichever region gives it | Store it as usual | | A bar before the edge's last close | 409 `out_of_order` | Drop it, or rebuild the window as a finished series | | An edge from another series or profile version | 409 `edge_mismatch` | Read this series' edge from your store | | A changed or truncated edge | 400 `edge_invalid` | Store edges as opaque strings, exactly as received | | Two writers on one series | Both answers are valid | Keep the first write: a version check in your store | | Your workload restarted | | Read the edge from your store and carry on | | You lost a series' edge | | Send the series again as a finished series with `return_edge: true` | The life of a forming series, on your side: ```mermaid stateDiagram-v2 [*] --> Forming: first extend, no edge [*] --> Forming: finished series with return_edge Forming --> Forming: extend with the stored edge Forming --> Forming: 409 or 400 (nothing applied, edge unchanged) Forming --> [*]: series retired: delete the edge ``` ### Backfill, with a webhook ```mermaid sequenceDiagram autonumber participant I as Ingest workload participant B as Your storage participant P as Patternode API participant W as Event receiver participant Q as Queue and workers participant S as Segment store I->>B: put the file; presign GET (source) and PUT (result) I->>P: POST /v1/segmentations/jobs {series, kind, format, source_url, result_url, return_edge} P-->>I: 202 job queued P->>B: GET source_url P->>B: PUT result_url P->>W: segmentation.job.succeeded (signed) W->>W: verify signature on raw body; reject stale; drop seen id W-->>P: 204 within 10 s W->>Q: enqueue job id Q->>B: read the result Q->>S: store segments (and the edge, to carry on live) Note over Q: on failure the message goes to the dead-letter queue ``` ### Rotating credentials without downtime ```mermaid sequenceDiagram autonumber participant A as Admin (person) participant P as Patternode API participant K as Key vault participant I as Ingest workload participant W as Event receiver A->>P: POST /v1/keys {kind: live, scopes, expires_in_days: 90} P-->>A: the new key, shown once A->>K: put as the current version I->>K: picks up the new version (restart or cache expiry) A->>P: GET /v1/keys: old key's last_used_at stops moving A->>P: DELETE /v1/keys/{old} A->>P: POST /v1/account/webhook_secret P-->>A: new secret; deliveries now carry both signatures A->>K: {current: new, previous: old} W->>K: accepts either signature ``` Next: [Decisions you make](decisions.md). --- ## Architecture overview For enterprise and solution architects: the whole picture in one page. Each section links to the detail an engineer needs, so you can stop reading at the level you need. ### The picture [![Patternode from the outside: your client's sources feed your ingest workload, which calls the Patternode API; segments come back into your store; signed events reach your event receiver](./img/context.svg)](./img/context.svg) Three parties: | Party | Owns | Runs | |---|---|---| | Your client (a utility, a fund, a fleet operator) | Assets, readings, the relationship with you | Historians, SCADA, meters, market feeds | | You, the partner | Your product, your brand, your data and your keys | An ingest workload, a segment store, an event receiver, your application | | Patternode | The service | The Patternode API: Delve today; Essence, Sentinel and Foresight on the same foundation | ### The architecture at a glance | Concern | Answer | |---|---| | Integration style | API-first. Your backend calls Patternode; Patternode answers and sends events. No agent, connector or software of Patternode's runs in your environment | | Direction | Outbound only from your side. Patternode never connects into your network; events reach an endpoint or event bus you choose | | Interfaces | REST and JSON (OpenAPI 3.1) on every plan; gRPC on Mission Critical and Dedicated; webhooks and event-bus delivery (AsyncAPI 3.1); bulk files (CSV, JSON Lines, Parquet, Arrow) read from and written to your storage | | Interaction | Synchronous for a finished series and for each extension of a forming series; asynchronous for backfill jobs and events | | What crosses | Bars (time, open, high, low, close) under a series name you make up. Segments come back. Nothing else | | Where data lives | Everything about your series is in your store: readings, segments, and for a forming series its edge. Patternode keeps none of it, not even between calls | | Residency | You choose a jurisdiction (Australia and New Zealand, the EU or the US); data never leaves it | | Identity | API keys on every plan; OAuth 2.0 client credentials with signed assertions, mutual TLS and workload identity on Mission Critical and Dedicated | | Resilience | Up to 99.99% availability, active-active across two regions, 60-second regional failover, nothing acknowledged lost ([Plans](../service/plans.md)) | | Semantics | Stateless: the same edge and bars always give the same answer, so retries are exactly-once in effect; at-least-once events; deterministic results per pinned profile version | | Change | One functional contract for every plan, product and industry; versioned in the path; changes announced in the [changelog](../resources/changelog.md) | ### One API, every product [![Delve is the foundation; Essence, Sentinel and Foresight are planned and run on Delve; one API, one key, one contract](./img/products.svg)](./img/products.svg) Integrate once. A series sent to Delve is not sent again for the products that run on it, and your keys, client, event receiver and infrastructure serve all of them. ### Architecture principles 1. Your data stays yours. Patternode receives the minimum (bars under opaque names) and keeps nothing, not even a forming series between calls. You store the results and the edge. 2. Outbound only. Nothing from Patternode connects into your network, which keeps it outside operational technology perimeters and simplifies your security review. 3. Contract first. The OpenAPI, AsyncAPI and gRPC files are the contract; this site, the mock server and the examples follow them. 4. One contract, many plans. Moving up a plan changes capacity and service levels, never your code. 5. Safe to retry. The same edge and bars always give the same answer, so a retry, a restart or a failover never double-counts or loses a bar. 6. Deterministic. The same bars with the same profile version give the same segments, always, so backtests, audits and live operation agree. 7. Your jurisdiction. Your series are processed only in the jurisdiction you choose, and support access stays there. 8. No lock-in. Segments and edges are already in your store, and the contract is published under Apache 2.0. ### Read next | You are | Read | |---|---| | An enterprise architect checking fit | [Security and data](security.md), [Deployment and resilience](deployment.md), the [service contract](../service/index.md) | | A solution architect designing the integration | [Integration architecture](integration.md), [Your side](partner-side.md), the [reference architectures](reference-architectures.md), [Decisions you make](decisions.md) | | An engineer building it | [Detailed flows](flows.md), the [Quickstart](../get-started/quickstart.mdx), the [API reference](/api/patternode-api) | | A project lead | The [onboarding playbook](../playbooks/delve-onboarding.md) | --- ## Integration architecture For solution architects. How your systems and Patternode's exchange data, and which style suits which need. ### Four styles [![Four integration styles: finished series, forming series, backfill and events, each with its calls and direction](./img/integration.svg)](./img/integration.svg) | Style | Interaction | Use for | Status | |---|---|---|---| | [Finished series](../guides/finished-series.mdx) | Request and response, one call per series window | History, galleries, reports, daily redraws | Available | | [Forming series](../guides/forming-series.mdx) | Request and response: the stored edge plus new bars in, the new edge out | Live tiles; acting as a segment closes | Preview | | [Backfill](../guides/backfill.mdx) | Asynchronous: a job reads your storage and writes the result back to it | Years of history, backtests, bodies over 5 MB | Available | | [Events](../guides/webhooks.mdx) | Pushed to you: webhook or event bus | Jobs finishing, account notices | Available (webhooks) | A typical integration combines them over a series' life: ```mermaid stateDiagram-v2 direction LR [*] --> Backfill: new series with history [*] --> Finished: new series, little history Backfill --> Forming: history stored Finished --> Forming: bars arrive live Finished --> Finished: redraw on a schedule Forming --> Forming: edge plus new bars Forming --> [*]: series retired, edge deleted ``` ### Interfaces and transports Every transport carries the same operations with the same semantics; the faster ones are optimisations, not different APIs. | Interface | Contract | Plans | Typical use | |---|---|---|---| | REST, JSON over HTTPS | `specs/openapi.yaml` | All | Everything; the reference | | Batched REST extensions | `specs/openapi.yaml`, `POST /v1/segmentations/extend/batch` | All | Up to 1,000 series in one request, each with its edge | | gRPC bidirectional stream | `specs/proto/patternode/delve/v1/delve.proto` | Mission Critical, Dedicated | Sustained high rates, low tail latency | | Webhooks, signed to Standard Webhooks | `specs/asyncapi.yaml`, channel `webhook` | Production and above | Job results, account notices | | Event bus (Amazon EventBridge, Kafka), CloudEvents 1.0 | `specs/asyncapi.yaml` | Mission Critical, Dedicated | Events into your own bus, without running a receiver | | Bulk files (CSV, JSON Lines, Parquet, Arrow IPC) | `POST /v1/segmentations/jobs`, links to your storage | All | Backfills and backtests | ### Synchronous and asynchronous | | Synchronous | Asynchronous | |---|---|---| | Calls | Finished series; each extension of a forming series | Jobs; every event | | Your side needs | A client that resends unchanged with backoff, and a store that keeps segments and edge in one write | A receiver or consumer that verifies, deduplicates and answers fast | | Ordering | Per series, by the edge's last close time | None; deduplicate and order by your own records | | Delivery | Exactly-once effect: same edge and bars, same answer | At least once; deduplicate by event id | ### Contract governance | Topic | Rule | |---|---| | Source of truth | The files in `specs/`. Pages, the mock and the examples follow them | | Versioning | In the path (`/v1/`). No breaking change to an `available` operation within a version; a new version runs beside the old for at least 12 months | | Status | Each operation and event carries `x-status`: `available`, `preview` (may change with 30 days' notice) or `planned` (specified, not yet served) | | Extensions | An industry-only addition is an OpenAPI Overlay in `specs/overlays/`, never a change to the core | | Compatibility | Clients must ignore unknown fields, enum values, event types and error codes | | Determinism | Results are fixed per profile version; versions are served for at least 24 months | Next: [Your side](partner-side.md), for what you build to use these interfaces. --- ## Your side, the reference architecture ## Your side: the reference architecture For solution architects and engineers. What a production integration needs in your environment. Any stack will do: what matters is the responsibility of each block and the contract it meets. [![The partner-side reference architecture: collector, bar builder, ingest workload, key vault, series registry, segment store, event receiver, queue and workers, your app, observability](./img/partner.svg)](./img/partner.svg) ### The building blocks | # | Block | Must | Example realisations | |---|---|---|---| | 1 | Collector | Read your sources in time order per series, and survive restarts without skipping or repeating | Historian client, feed handler, file watcher | | 2 | Bar builder | Turn readings into bars: ordered, non-overlapping, values above zero (offset if needed), gaps left as gaps | Your code; `examples/python/bars.py` | | 3 | Ingest workload | Call Patternode from your backend only: key header, body hash, idempotency key, retries with backoff and `retry-after`, the stored edge with each new bar, one writer per series | A service or function; `examples/*/client` | | 4 | Key vault | Hold the live key (or OAuth signing key) and the webhook secret encrypted; readable only by blocks 3 and 7; versioned for rotation | AWS Secrets Manager, Azure Key Vault, Google Secret Manager, HashiCorp Vault | | 5 | Series registry | Map each opaque series name to your client and asset; hold each forming series' edge with a version, in the same write as its segments | A table in your database; [the segment store](../guides/segment-store.mdx) | | 6 | Segment store | Keep segments by client prefix, series, level and id; replace provisional segments, keep final ones; delete a client by deleting its prefix | Object storage, a time-series or relational database | | 7 | Event receiver | Verify the signature on the raw body, reject stale timestamps, drop seen event ids, answer 2xx within 10 seconds, queue the work | A function behind HTTPS; or your event bus | | 8 | Queue and workers | Do the work after the answer; retry; park failures in a dead-letter queue | SQS, Pub/Sub, Service Bus, Kafka | | 9 | Your application | Read segments from your store, never from Patternode; draw each chart at the level it can read | Your product | | 10 | Observability | Measure success rate, p99 latency, 429 rate, edge and out-of-order errors, event lag and dead-letter depth; propagate `traceparent` | Your monitoring and tracing stack | [AWS with Terraform](../guides/aws-terraform.md) builds blocks 4, 6, 7 and 8 and the policy for 3. ### What Patternode assumes about your environment | # | Assumption | If it does not hold | |---|---|---| | A1 | Your backend can make outbound HTTPS calls (and gRPC, if you use it) to your jurisdiction's endpoint | Use private connectivity, or an egress proxy that allows `*.api.patternode.com` | | A2 | You have a secret store, and workloads read credentials from it at start | Introduce one before go-live; keys in code or images fail the [production checklist](../go-live/production-checklist.md) | | A3 | You can identify each series stably, and keep the mapping from opaque name to client and asset yourself | Patternode cannot recover it: it never learns who or what a series is | | A4 | You can deliver each series' bars in time order, with one writer per series, keeping its edge | Funnel each series through one writer (a partition key, a lock, an actor); a bar before the edge is refused. Lose an edge and you can rebuild it from the series' bars | | A5 | You can store segments: plan for up to one segment record per bar you send, across all levels | Store only the levels you draw, and re-fetch history as a finished series when needed | | A6 | Your bar times come from your sources; Patternode never changes them | Normalise time zones and clock offsets before building bars | | A7 | An HTTPS endpoint can receive webhooks, or you use an event bus (Mission Critical) | Poll jobs instead of receiving webhooks; slower, but complete | | A11 | For jobs, storage you can presign links in (S3, Azure Blob, Google Cloud Storage) | Send series inline in pieces of up to 5 MB instead | | A8 | You can monitor and alert on your side of the integration | Build it before go-live; Patternode's status page covers only Patternode's side | | A9 | For Mission Critical: your callers run in two regions of the jurisdiction's cloud, or connect privately | A single region of yours caps your availability below the service's | | A10 | Two named technical contacts, and an on-call rota for Mission Critical | Required by the order form | ### Sizing | Input | How to estimate | |---|---| | Bars a second at peak | Series × (1 / bar width) × peak factor. Peaks: a storm (every point reporting), a market open or close | | Edges to keep | One per forming series, a few kilobytes each | | Segment store growth | Up to one segment record per bar sent, a few hundred bytes each, plus an edge per forming series | | Events a second | Jobs finishing plus segments closing at the levels you subscribe to | Compare the peaks with your [plan](../service/plans.md). Next: [Deployment and resilience](deployment.md). --- ## Flows for a fund ### Backtest, then go live from where it ended ```mermaid sequenceDiagram autonumber participant R as Research platform participant B as Your storage participant P as Patternode participant M as Model registry participant L as Live platform R->>B: Parquet per instrument; presign GET and PUT R->>P: POST /v1/segmentations/jobs {profile: standard@2026-11, return_edge: true, links} P->>B: GET the file, PUT the result (segments and the edge at its end) P-->>R: segmentation.job.succeeded R->>B: read results; build features; backtest R->>M: model, window, profile version Note over L: at the session start L->>P: POST /v1/segmentations {today so far, profile, return_edge: true} P-->>L: segments and an edge per instrument L->>L: edges into memory and the durable store ``` ### The intraday loop ```mermaid sequenceDiagram autonumber participant F as Bar builder participant X as Extender participant E as Edge store participant P as Patternode (gRPC Extend) participant S as Signal engine loop each bar close, every instrument F->>X: closed bars X->>P: ExtendRequest {correlation_id, series, edge, bars} per instrument P-->>X: ExtendResponse {closed, right_edge, edge} X->>E: new edge, version n+1 (if still n) X->>S: closed segments and right edge end ``` ### Recovering an extender ```mermaid sequenceDiagram autonumber participant X as New extender participant E as Edge store participant P as Patternode X->>E: read edges for its instruments alt an edge is present X->>P: extend from it with the next bars else an edge is missing X->>P: POST /v1/segmentations {session so far, return_edge: true} P-->>X: an edge X->>E: store it end ``` The general flows, with every error and what to do, are in [Detailed flows](../flows.md). --- ## Quant fund architecture For a fund's CTO, head of quant technology and their architects, then its engineers. These views apply the general [architecture](../index.md) to a fund that segments trades and bars across many instruments, in research and in live trading. [![A quant fund with Patternode: venues and vendors feed the fund's data platform; research runs jobs over files in the fund's storage; the live platform extends each instrument from its stored edge; execution sends orders to brokers; the segment store holds segments and edges for risk and compliance](../img/quant-context.svg)](../img/quant-context.svg) ### At a glance, for a fund | Concern | Answer | |---|---| | What Patternode sees | Bars (times and four prices) under instrument names you choose. Never positions, orders, signals, models or strategies | | What Patternode keeps | Nothing. Research files stay in your storage; live state stays in your edges | | Research | Jobs read Parquet or Arrow from your storage and write results back; a job per series, run in parallel | | Live | Each instrument extended from its stored edge as its bar closes, over gRPC on one connection; p99 10 ms at the edge on Mission Critical | | Time | Nanosecond timestamps, kept exactly as sent | | Reproducibility | The same bars with the same profile version give the same segments, bit for bit, for at least 24 months. Backtest and live agree | | Change | No change in your cells during your venues' trading hours (blackout periods) | | Resilience | Patternode holds no state, so either region answers any call; a regional failover takes up to 60 seconds and loses nothing of yours | | Regulation | Terms for DORA and UK outsourcing rules, model risk documentation per profile version, an exit plan ([Compliance](../../service/compliance.md)) | | Plan | Production for research; Mission Critical for live; Dedicated at full scale or under critical third-party oversight | ### The views | View | For | Covers | |---|---|---| | [Research and backtest](research.md) | Quant researchers' platform team | Jobs over your storage, features, backtests, handing over to live | | [Live trading](live.md) | Trading platform engineers | The live path, edges in memory and at rest, the latency budget, failure handling | | [Reproducibility](reproducibility.md) | Model risk, research leads | Profile versions, pinning, moving to a new version safely | | [Operations and compliance](operations.md) | CTO, risk, compliance, operations | Placement, capacity, blackout periods, resilience, DORA, audit | | [Flows](flows.md) | Engineers | Every call in backtest, go-live, intraday and recovery | --- ## Live trading [![Live trading: feed handler, bar builder, extender with an edge cache and a durable edge store, gRPC Extend at Patternode, signal engine, execution; below, an example latency budget of 22 ms from bar close to order](../img/quant-live.svg)](../img/quant-live.svg) ### The path 1. The feed handler normalises trades and quotes with nanosecond times. 2. The bar builder closes each instrument's bar on time. 3. The extender sends each closed bar with that instrument's edge. One gRPC connection carries every instrument (Mission Critical); batched REST does the same on any plan, up to 1,000 instruments a request. 4. The answer's closed segments and right edge go to the signal engine; its new edge replaces the old one in memory and in the durable edge store. 5. Signals go to execution as usual. ### Edges, in memory and at rest | Where | Why | Rule | |---|---|---| | In memory, in the extender | Speed: the next bar needs it at once | One extender owns each instrument: partition instruments across extenders | | A durable store (a key-value store, the segment store) | Restarts and failover of your own platform | Write the new edge before acting on the signal, with a version check, so a restarted extender never sends a stale edge | If an edge is ever lost, send the instrument's session so far as a finished series with `return_edge: true`, and carry on from that edge. Nothing is lost at Patternode, because Patternode held nothing. ### The latency budget Put your platform in the cloud region of your jurisdiction and connect privately; then the network is a millisecond or two each way. Patternode's p99 at the edge is 10 ms on Mission Critical (5 ms by agreement on Dedicated), and every answer's `server-timing` header gives Patternode's share, so you can see which part of a slow trade was whose. Measure your own parts the same way, and alert on the sum. ### When something fails | Failure | What happens | Your platform | |---|---|---| | A Patternode region | Up to 60 seconds of resets or 503s, then answers from the other region | Resend the unanswered requests unchanged; the answers are the same | | Patternode answers slowly | 429 or 503 with `retry-after` if over your rate | Resend after the wait; decide in advance whether a late segment still trades | | A late bar | 409 `out_of_order`; nothing applied | Drop it; the edge is unchanged | | Your extender restarts | | Read edges from the durable store and carry on | | A bad tick makes a bad bar | It is applied: segments are final once closed | Filter bad ticks before the bar builder | Next: [Reproducibility](reproducibility.md). --- ## Operations and compliance ### Placement and plan | Platform | Plan | Placement | |---|---|---| | Research | Production | Any region of your jurisdiction; jobs need only links to your storage | | Live | Mission Critical | Your live platform in the jurisdiction's cloud region, two regions if your own availability needs it; private connectivity | | Live at full scale, or under critical third-party oversight | Dedicated | As Mission Critical, with cells for you alone and your change board | ### Capacity Size for the open and the close, when every instrument closes a bar at once: instruments × bars a second at that moment. Compare with your plan's bars a second ([Plans](../../service/plans.md)); tell Patternode 10 business days ahead to go beyond it. Research jobs are bounded by concurrent jobs, not by rate. ### Blackout periods Register your venues' trading hours (and any other window, such as an index rebalance) as blackout periods. Patternode releases nothing to your cells inside them except a Severity 1 fix, and runs its quarterly regional failover tests outside them. ### Resilience Patternode holds none of your state, so its failures cannot lose your data, and either region answers any call. Your platform's resilience is yours: edges in a durable store, extenders that restart from it, and callers in two regions if you need them. See [Deployment and resilience](../deployment.md). ### Regulation | Regime | What Patternode gives you | |---|---| | DORA (EU) | Article 30 terms in the order form: service levels, locations, access, audit, exit; incident notification on its timelines; resilience test results; subprocessor list | | UK operational resilience and outsourcing | The same terms; recovery objectives you can use for impact tolerances | | MiFID II RTS 25, SEC Rule 613 | Your nanosecond times kept exactly; Patternode's own clocks within 100 microseconds of UTC | | Model risk (SR 11-7, PRA SS1/23) | Determinism per version; version documentation under NDA ([Reproducibility](reproducibility.md)) | | Confidentiality of strategies | Patternode sees only bars under names you choose, and keeps none of them | ### Support Mission Critical and Dedicated: 24x7, Severity 1 answered in 15 minutes by phone, a shared channel, and post-incident reports within 5 business days ([Support](../../service/support.md)). Next: [Flows](flows.md). --- ## Reproducibility [![Reproducibility across profile versions: standard@2026-11 for backtest and live, standard@2027-05 published later; research re-run and shadowed live on the new version, then a switch; the old version served at least 24 months after its successor](../img/quant-versions.svg)](../img/quant-versions.svg) | Promise | What it means for you | |---|---| | The same bars and profile version give the same segments, bit for bit | A backtest's segments are exactly the segments live trading would have had | | A profile version never changes | A result stored with its version can be reproduced at any time | | Old versions are served for at least 24 months after a new one | You choose when to move, and can re-create any result in that time | | A new version is announced in the changelog | Nothing changes under you | ### Pinning Name the version in every request: `standard@2026-11`, never `standard`, in research and live. Record it beside every stored result and in the model registry with the model it fed. An edge is sealed with its version, and is refused with another (409 `edge_mismatch`). ### Moving to a new version 1. Re-run research on the new version as jobs, into a separate path of your storage. 2. Re-validate the models that use the segments, as your model risk policy asks. 3. Shadow live: run the new version beside the old for a period, from its own edges (a finished series of the session so far, with `return_edge`), and compare. 4. Switch the instruments to the new version's edges, and keep the old results with their version for audit. ### For model risk For each profile version, Patternode provides under NDA a description of its inputs, outputs, known limits and changes from the previous version, for your model inventory and validation (SR 11-7, PRA SS1/23). The version string is the identifier to cite. Next: [Operations and compliance](operations.md). --- ## Research and backtest [![Research and backtest: tick store, bar builder, the fund's storage, job runner; Patternode jobs read the file and write the result back; features and backtests read the results; the model registry records versions and edges](../img/quant-research.svg)](../img/quant-research.svg) ### The path 1. Build bars from your tick store, point in time, with nanosecond times. A second (or minute) with no trade is a gap, not a flat bar. 2. Write each series to Parquet in your storage, under a path that names the profile version: `out/standard@2026-11/eq/exmpl/1s.json`. 3. Presign a GET for the file and a PUT for the result, and submit a job per series with `profile` pinned. Run as many in parallel as your plan's concurrent jobs. 4. Take `segmentation.job.succeeded` from your webhook or event bus, and read the result from your storage. 5. Build features from the segments (levels, trends at several timescales, shapes) and backtest. 6. Record in the model registry the data window, the profile version, and for each series the edge at the end of the window (`return_edge: true` on the last job). ### Choices | Choice | Recommendation | |---|---| | Bar width | The finest your strategy acts on; coarser views come from the higher levels, not from wider bars | | One job per | Series and window. Re-running a window gives the same result, so a failed or lost run is re-run, not repaired | | Storage layout | By profile version, then instrument, then bar width: results from two versions never mix | | Cost | Charged by samples; a failed job is not charged; re-runs of a window cost the same each time, so cache results in your storage | ### What reaches Patternode The bars of each series, read from your link for the time of the job, and nothing else. No universe definitions, no fundamentals, no labels, no strategy. Results are written to your link and not kept. Next: [Live trading](live.md). --- ## Reference architectures For solution architects. Where Patternode sits among the systems your industry already runs. Both use the same API; the [industry pages](../industries/index.md) have the examples and data. ### Utility operations [![Utility reference: operational zone with field devices, SCADA and historian; a one-way transfer to a historian mirror in the DMZ; the ingest workload in the enterprise zone calls Patternode ANZ over private connectivity; events return to a receiver in the enterprise zone](./img/utilities.svg)](./img/utilities.svg) | Decision | Here | |---|---| | Where data is read | A historian mirror in the DMZ, fed one way from the operational zone. Nothing reads the operational zone directly | | What is sent | Bars built from the points the historian kept, good quality only, every 15 minutes or as configured | | Where Patternode sits | Outside every control zone and electronic security perimeter, reached outbound from the enterprise zone or your cloud | | Plan and connectivity | Mission Critical for control room use, in your jurisdiction, with private connectivity and mutual TLS | | Events | To a receiver in the enterprise zone; nothing flows back toward the operational zone | | Operations | Storm and emergency declarations registered as blackout periods; capacity sized for the storm | More: [Utilities](../industries/utilities.mdx). ### Quantitative trading [![Trading reference: market data through a feed handler; the live path builds bars and extends each instrument from its stored edge over gRPC to Patternode in the same region, the answers feeding the signal engine; the research path runs jobs over Parquet in your storage for backtests; the same pinned profile version on both](./img/finance.svg)](./img/finance.svg) | Decision | Here | |---|---| | Live path | One-second to one-minute bars, nanosecond times, each instrument extended from its stored edge over gRPC; the answers feed the signal engine directly | | Research path | Jobs over Parquet in your storage, results written back to it; segments stored per profile version, with each series' edge to go live from | | Reproducibility | The same pinned profile version on both paths: backtest segments are the segments you would have had live | | Placement | Your platform in the jurisdiction's cloud region; private connectivity; p99 10 ms at the edge on Mission Critical | | Operations | Venue trading hours registered as blackout periods; capacity sized for the open and the close | More: [Finance](../industries/finance.mdx), and the full set of [quant fund views](quant/index.md). Next: [Detailed flows](flows.md). --- ## Security and data For enterprise architects, security architects and reviewers. [![Trust boundaries from your workload through transit and Patternode's edge to the service: bars and an opaque name cross, segments come back, events come back signed](./img/security.svg)](./img/security.svg) ### Controls at each boundary | Boundary | Controls | |---|---| | Your trust boundary | Credentials in your secret store; scoped, expiring keys per workload; or OAuth 2.0 client credentials with `private_key_jwt`, so no long-lived secret exists; secret scanning for the published key pattern | | In transit | TLS 1.3 (1.2 with forward secrecy only for clients that need it); optional mutual TLS; private connectivity; an IP allow list per key | | Patternode's edge | Credential checked before anything runs; SHA-256 of every write body checked; scopes checked; rate limits, quotas and spend limits enforced; a request id on every answer | | Patternode's service | Keeps none of your series; seals each edge (encrypted and signed, bound to your account, series and profile version), optionally with your own KMS key; an audit log of every account change and support access, kept 7 years | | Back to you | Events signed with HMAC-SHA256 to Standard Webhooks, with a timestamp to reject replays and overlapping secrets for rotation | ### Data | Data | Crosses to Patternode | Kept by Patternode | |---|---|---| | Readings | As bars (times and four values) | No | | Series names | Yes, opaque names you make up | With usage records | | Client, asset, units, location, labels | Never | Never | | Segments | Returned to you, or written to your storage by a job | No | | Edges | Returned to you sealed; sent back by you with the next bars | No | | Keys | Created at Patternode and shown once | A keyed hash only; Patternode cannot recover a key | | Usage, audit | | Usage for billing; audit events for 7 years | You need send no personal data at all. Patternode signs a data processing agreement in any case. ### For your review | Question | Answer | |---|---| | Does anything of Patternode's run in our environment? | No. You run your own code, from published examples if you like | | Does Patternode connect into our network? | No. Events reach an endpoint you expose or your event bus; jobs use links you sign, for the files you choose | | Does Patternode keep our data? | No. Not series, segments, edges or results; only account, usage and audit metadata | | Can we keep traffic off the internet? | Yes, with private connectivity on Mission Critical and Dedicated | | Can we hold the encryption key? | Yes, on Mission Critical and Dedicated: your key seals your edges, so revoking it makes them unreadable to Patternode | | Assurance | SOC 2 Type II and ISO/IEC 27001, an annual penetration test, a subprocessor list ([Compliance](../service/compliance.md)) | | Regimes | DORA, UK operational resilience, NERC CIP, IEC 62443, the privacy acts ([Compliance](../service/compliance.md#regimes)) | Next: [Reference architectures](reference-architectures.md). --- ## Data boundary Patternode keeps none of your series: no copies, no checkpoints, no results. | | Finished series | Forming series | |---|---|---| | Patternode receives | Bars and a series name | Bars, a series name, and the edge you stored | | Patternode keeps | Nothing of the series. Usage records (counts, key, time) for billing | The same: nothing. The series' state is in its edge, which you keep | | Jobs | Read from a link to your storage, written to a link to your storage; the job record holds only status and usage | | | Patternode never sees | Who the client is, what the asset is, units, location, labels | The same | ### Naming series The series name is the only text you send. Make it opaque and stable: ```python h = lambda s: hashlib.sha256((SALT + s).encode()).hexdigest()[:12] name = f"{h(client_id)}/{h(asset_id)}/{measure}" # e.g. 7f3a9c1b02e4/91cd0e55aa21/ac-power ``` - Keep `SALT` secret and per environment, so names cannot be matched to your records by anyone else. - Keep the mapping from name to client and asset in your own store. - The measure suffix is optional. Leave it out if even the kind of measure is sensitive. ### Your client's prefix Store each client's readings and segments under that client's own prefix (a bucket prefix, a schema, a tenant id). Your app then reads only from the prefix of the client it is serving, and deleting a client is deleting a prefix. The [AWS recipe](../guides/aws-terraform.md) creates one bucket for every client; keep the prefix in every key you write. --- ## Products | Product | What it is for you | Runs on | In the API | |---|---|---|---| | Delve | The shards toolkit. Turns a time series into segments at every level of its tree, so each chart draws the level it can read: every series in a gallery, one series at every level, live tiles, and later shape search | | Segmentations and jobs (available); extensions of forming series (preview) | | Essence | Patterns by precedent: what repeats | Delve | Planned | | Sentinel | Anomalies by shape: what moves, and the alarm on it | Delve | Planned | | Foresight | Forecasting: what comes next | Delve | Planned | Delve was called Mosaic until 2026. Older material may use that name; it is the same product. ### Delve | | | |---|---| | You send | A series as bars or samples, finished in one call or forming one bar at a time | | You get | Segment boxes at every level, the open right edge marked provisional; for a forming series, whether each bar extended or segmented each level | | You keep | The segments and edges, in your own store, beside your own data | | Patternode keeps | Nothing. A forming series' state travels with you as its sealed edge | | Planned | Shape search across a gallery; a drawing kit (gallery, single view with levels, live tile) in your brand | The status of each route is its `x-status` in the [API reference](/api/patternode-api): `available`, `preview` or `planned`. --- ## Requests, errors and limits ### Authentication Send the key as `Authorization: Bearer `, with one space after `Bearer`, or as `x-api-key: `. A request to `/v1/` whose key has the wrong prefix or length is refused before anything runs (401 `missing_api_key`). ### Writes carry the body's SHA-256 Every `POST`, `PUT` and `PATCH` to `/v1/` sends the SHA-256 of the exact body bytes, lowercase hex, in `x-amz-content-sha256`. With no body, send the hash of the empty string, `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`. Hash the bytes you send, not a re-serialisation of them. | Without it | With the wrong hash | |---|---| | 400 `missing_content_sha256`, from the edge | 403, from the edge, not in the API's error shape | ### Idempotency Send `Idempotency-Key` on every `POST`, a fresh UUID per operation and the same one on every retry of it. For 24 hours Patternode keeps the key and a hash of the request, never its data: a repeat is not billed twice, and the same key with a different body is 422 `idempotency_conflict`. Segmentations are recomputed on a repeat, and give the same answer, because they are deterministic. ### Every response `request-id` is on every response. Quote it to support. ### Errors ```json { "type": "error", "error": { "type": "invalid_request_error", "message": "...", "details": { "error_code": "invalid_series", "problems": [{ "index": 3, "problem": "times_not_increasing" }] } }, "request_id": "req_..." } ``` Branch on `error.details.error_code`; the message is for people and may change. | Status | `error.type` | `error_code` | Do | |---|---|---|---| | 400 | `invalid_request_error` | `invalid_series` (with `problems`), `body_too_large`, `missing_content_sha256` | Fix the request. For `body_too_large`, run a job from your storage | | 401 | `authentication_error` | `missing_api_key`, `invalid_api_key`, `key_disabled`, `key_expired` | Check the secret store; rotate | | 402 | `billing_error` | `credit_balance_too_low` | Add credit, or turn on auto-reload | | 403 | `permission_error` | `missing_scope`, `test_key_demo_only`, `terms_not_accepted` | Use the right key | | 404 | `not_found_error` | `not_found` | | | 400 | `invalid_request_error` | `edge_invalid` | The edge was changed or is not Patternode's: use the one you stored | | 409 | `conflict_error` | `out_of_order`, `edge_mismatch` | A bar before the edge's last close, or an edge from another series or profile version. Nothing was applied | | 422 | `invalid_request_error` | `idempotency_conflict` | A new operation needs a new key | | 429 | `rate_limit_error` | `rate_limited`, `enforced_spend_limit_reached`, `customer_spend_limit_reached` | Wait for `retry-after`. Spend limits do not clear by waiting: raise the limit | | 503 | `overloaded_error` | `service_capacity` | Wait for `retry-after` | | 5xx | `api_error` | `internal` | Retry with backoff | ### Rate limits Each response carries `ratelimit-policy` and `ratelimit` (IETF RateLimit headers), so your client can slow down before it is refused. Limits are per account and per key: requests a minute, samples a minute and concurrent jobs, set by your plan. Your onboarding pack gives your tier's numbers. ### Retries Retry 429, 503 and other 5xx, and network errors, with exponential backoff and jitter, honouring `retry-after`, and with the same `Idempotency-Key`. Never retry other 4xx unchanged. The example clients do this. --- ## Shards and bars ### What you send: bars A bar is one interval of your series: ```json [open_time, close_time, open, high, low, close] ``` Times are epoch milliseconds, UTC. The rules: | Rule | Why | |---|---| | In time order, not overlapping | A series has one past | | `low <= open, close <= high` | The four values describe the interval | | Every value above zero | A contract constraint. If your measure crosses zero, add a fixed offset before sending and remove it when you draw | | Leave gaps; never send a flat filler bar | An interval where nothing happened, too few readings, nothing moved, or nothing flowed (an inverter at night), is absent, not zero. Irregular spacing is expected and read well | You can send samples, `[time, value]`, instead, and the service makes bars. Sending bars keeps the choice of width with you, and is the better default. `examples/python/bars.py` builds them. A shard is a fragment of a time series that keeps its shape at any scale. Each segment Delve returns is one shard. ### What you get: segment boxes ```json { "id": "h2@1730246400000", "parent": "h3@1730246400000", "height": 2, "order": 0, "bars": 14, "open_time": 1730246400000, "open": 412.0, "high_time": 1730271600000, "high": 455.2, "low_time": 1730250000000, "low": 401.7, "close_time": 1730296799000, "close": 448.3, "provisional": false } ``` | Field | Meaning | |---|---| | `height` | The level. 1 is the finest segments above your bars; higher is coarser. The top is the whole series | | `id` | `h@`. Stable: the same segment has the same id in a segmentation and in every extension | | `parent` | The segment one level up that contains it | | `bars` | How many of your bars it covers | | open, high, low, close with times | The four vertices of its shape | | `provisional` | The open right edge at its level: it may still grow and its close may move. Every other segment is final | ### Drawing - A chart of width W pixels over a time window shows the coarsest level whose segments are still wide enough to read in that window, and switches to finer levels as the viewer zooms in. Store all levels; pick per chart. - Draw a segment as a box from `open_time` to `close_time` between `low` and `high`, and its line from open through high and low, in time order, to close. - A trend is the same series at a coarser level, drawn faintly behind. - The open segment at levels 1, 2 and 3 gives a reading at three timescales: rising if its close is above its open, falling if below, steady if close. ### Finished and forming | | Finished | Forming | |---|---|---| | You have | The whole series, or a window of it | One more bar at a time | | You call | `POST /v1/segmentations` (or a job from your storage) | `POST /v1/segmentations/extend`, with the edge | | You get | Every segment | The segments the bars closed, the right edge, and a new edge to store | | Same result | Yes: appending every bar gives the same segments as sending them all at once | | --- ## Versioning and status The version is in the path (`/v1/`). Within a version: | Change | Notice | |---|---| | A new route, a new optional field, a new event type, a new `error_code` | None. Ignore what you do not know | | A breaking change to an `available` route | Never within `v1`. A `v2` would run beside `v1` for at least 12 months | | A change to a `preview` route | 30 days, in the [changelog](../resources/changelog.md) and by email to technical contacts | | A `planned` route | Specified to design against; it answers 404 until it ships | Every operation in `specs/openapi.yaml` and every message in `specs/asyncapi.yaml` carries `x-status`. Write clients that tolerate unknown fields and unknown enum values. --- ## account.balance_low The credit balance fell below your threshold. | | | |---|---| | Status | planned | | Channels | `webhook` | ### Payload | Field | Type | Required | Description | |---|---|---|---| | `type` | `account.balance_low` | Yes | | | `created_at` | string | | | | `data` | Account | Yes | | --- ## Events The events Patternode sends to your HTTPS endpoint. Set the endpoint with `PATCH /v1/account` (`webhook_url`), or per job in `webhook_url`, and get the signing secret with `POST /v1/account/webhook_secret`. Webhook deliveries are signed to Standard Webhooks; see [Receiving webhooks](../guides/webhooks.mdx). Every event has a stable `id` to deduplicate on, and stream events a `sequence` to order on ([Real-time](../service/realtime.md)). The contract is [`/specs/asyncapi.yaml`](pathname:///specs/asyncapi.yaml). | Event | What | Channels | Status | |---|---|---|---| | [`segmentation.job.succeeded`](./segmentation-job-succeeded.md) | A segmentation job succeeded | `webhook` | available | | [`segmentation.job.failed`](./segmentation-job-failed.md) | A segmentation job failed | `webhook` | available | | [`key.expiring`](./key-expiring.md) | A key expires within 7 days | `webhook` | planned | | [`account.balance_low`](./account-balance-low.md) | The credit balance fell below your threshold | `webhook` | planned | --- ## key.expiring A key expires within 7 days. | | | |---|---| | Status | planned | | Channels | `webhook` | ### Payload | Field | Type | Required | Description | |---|---|---|---| | `type` | `key.expiring` | Yes | | | `created_at` | string | | | | `data` | Key | Yes | | --- ## segmentation.job.failed A segmentation job failed. | | | |---|---| | Status | available | | Channels | `webhook` | Not charged. data.error says why; error.details.problems lists bad rows. ### Payload | Field | Type | Required | Description | |---|---|---|---| | `id` | string | Yes | | | `type` | `segmentation.job.failed` | Yes | | | `created_at` | string | | | | `data` | Job | Yes | | --- ## segmentation.job.succeeded A segmentation job succeeded. | | | |---|---| | Status | available | | Channels | `webhook` | The result is in your storage, at the result_url you gave. ### Payload | Field | Type | Required | Description | |---|---|---|---| | `id` | string | Yes | | | `type` | `segmentation.job.succeeded` | Yes | | | `created_at` | string | | | | `data` | Job | Yes | | ### Example ```json { "id": "evt_01JBX8F3TB", "type": "segmentation.job.succeeded", "created_at": "2026-10-30T02:06:52Z", "data": { "object": "segmentation_job", "id": "job_01JBX8F3TA", "status": "succeeded", "series": "exu/7f3a9c/1b02e4/export-kw", "created_at": "2026-10-30T02:06:10Z", "finished_at": "2026-10-30T02:06:51Z", "result_written": true, "usage": { "samples_billed": 2190000, "cost_usd": 4.38 }, "error": null } } ``` --- ## Getting access There are three levels of access. You can start on the first today, without asking anyone. | Level | What you can do | How to get it | How long | |---|---|---|---| | 1. No account | Read everything here, generate clients from the specs, run every example against the mock server | Nothing: clone the repository | Now | | 2. Evaluation | Call the real API with a test key (demonstration series, free) and a live key with a small credit, on the Evaluation plan | Ask for an evaluation account (below) | 2 business days | | 3. Production | Your own series in production, on the plan you choose | An order form, then the [onboarding playbook](../playbooks/delve-onboarding.md) | From a week, depending on the plan | ### Level 1: no account ```bash git clone https://github.com/patternode/patternode-developers cd patternode-developers python mock/mock_patternode.py & PATTERNODE_API_KEY=pnk_test_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA python examples/python/first_call.py ``` Then the [Quickstart](quickstart.mdx). Everything you build against the mock works unchanged against the real API. ### Level 2: ask for an evaluation account Email `partners@patternode.com` with the subject "Evaluation account", and say: | Tell us | Why | |---|---| | Your organisation and website | The account is in its name | | Two technical contacts: name, work email, role | They receive the keys ([Getting your API keys](api-keys.md)) | | Which products: Delve now; Essence, Sentinel or Foresight when they open | To set up your account | | Your industry and the kind of data (historian tags, meter reads, trades, bars) | So the onboarding pack suits it | | The jurisdiction your data must stay in: Australia and New Zealand, the EU or the US | Your account's home ([Resilience](../service/resilience.md#jurisdictions)) | | Roughly how many series, and how often a bar closes | To set your quotas | Do not send data, keys or anything confidential by email. Patternode answers within 2 business days, and then: 1. Both contacts get the welcome email: account id, plan, endpoint, quotas and the test key. 2. The first contact gets the one-time link to the admin key; its passphrase is read out on a short kick-off call. 3. You follow stages 2 to 6 of the [onboarding playbook](../playbooks/delve-onboarding.md). ### Level 3: production When you are ready, tell your Patternode contact the plan ([Plans](../service/plans.md)), the jurisdiction and the transports you need. Patternode sends an order form; once it is signed, your production account is created and the onboarding pack arrives as for evaluation. The [onboarding playbook](../playbooks/delve-onboarding.md) takes you from there to go-live. ### Help | Need | Where | |---|---| | A question about integrating | `partners@patternode.com`, or your shared channel once you have one | | A mistake in these docs or examples | A [GitHub issue](https://github.com/patternode/patternode-developers/issues/new/choose) | | A production problem | [Support](../service/support.md), by your plan's channel | | A security issue | `security@patternode.com` ([SECURITY.md](https://github.com/patternode/patternode-developers/blob/main/SECURITY.md)) | | Service status | `status.patternode.com` | --- ## Getting your API keys Patternode issues you two keys to start: a test key, and an admin key you use once to make your own. Patternode never sees, sends or stores your live keys in readable form. It keeps only a keyed hash of each key, so it cannot recover a key for you, only help you replace it. ### The three kinds | Kind | Prefix | Can | Cannot | |---|---|---|---| | Test | `pnk_test_` | Run the demonstration series (`{"demo": "walk"}`, `{"demo": "sensor"}`); free | Run your own series | | Live | `pnk_live_` | Run your own series; billed from your credit | Manage keys or the account | | Admin | `pnk_admin_` | Create, disable and delete keys; set limits, the webhook URL and auto-reload; read usage; buy credit | Run anything | A key is its prefix, 30 random characters and a 6-character checksum. Every key matches ```text \bpnk_(live|test|admin)_[0-9A-Za-z]{36}\b ``` which is published so that secret scanners (GitHub secret scanning, gitleaks, trufflehog) can find a leaked key. Add it to yours. ### How you receive them | Step | What happens | Channel | |---|---|---| | 1 | The order form names two technical contacts | Order form | | 2 | Patternode creates your account, sets your tier and profiles | | | 3 | Both contacts get the welcome email: account id, tier, profiles, base URL and the test key | Email | | 4 | The first contact gets a one-time link to the admin key. It opens once, then expires; unopened, it expires after 72 hours | Email, one-time link | | 5 | The passphrase for that link is read out on the kick-off call, never written down | Voice | | 6 | You make your own admin key with the delivered one, then delete the delivered one ([playbook, stage 5](../playbooks/delve-onboarding.md#stage-5-take-control-of-your-credentials)) | The API | | 7 | You create live keys yourself, straight into your secret store | The API | So the only key that crosses a channel you do not control is the delivered admin key, it crosses once, behind a code spoken on a call, and it is deleted within the hour. If the link has expired or was opened by someone else, tell Patternode at once: the admin key is revoked and a new link sent. Patternode will never ask you for a key, by email, chat, call or a support ticket. A message that does is not from Patternode. ### Scopes Scopes are fixed when a key is created. A preset is the usual choice. | Scope | Grants | |---|---| | `segment:run` | `POST /v1/segmentations`, `/extend`, `/extend/batch`, `/jobs` | | `segment:read` | `GET /v1/segmentations/jobs/{id}` | | `usage:read` | `GET /v1/account`, the usage and cost reports | | `keys:manage` | Keys, limits, webhook URL and secret | | `billing:manage` | Credit purchase, auto-reload | | Preset | Scopes | For | |---|---|---| | `full` | `segment:run`, `segment:read` | An ingest workload | | `read-only` | `segment:read` | A dashboard that checks job status | | `admin` | `usage:read`, `keys:manage`, `billing:manage` | Admin keys only | Give each workload and each environment its own live key, with the narrowest preset that works. A leaked key is then one workload's problem, and its usage is visible on its own line of the usage report. ### Expiry and limits Expiry is chosen at creation (1, 7, 30 or 90 days, a date, or never) and cannot be changed. Use 90 days for live keys and rotate before they expire; use 1 or 7 days for a key given to a contractor or a CI job. A key can also carry its own monthly spend limit and request rate, below the account's. ### Where to keep them | Key | Keep it in | Read by | |---|---|---| | Admin | Your password manager, shared by the two technical contacts | People, by hand | | Live | Your cloud secret store (AWS Secrets Manager, Google Secret Manager, Azure Key Vault, Vault) | The ingest workload's role, at start | | Test | Your CI secrets | CI | | Webhook secret | Your cloud secret store | The webhook receiver | Never in source code, container images, `.env` files that are committed, client-side code, logs or tickets. Send keys only from your backend: a key in a browser or a mobile app is a published key. ### Rotating Live key, with no downtime: 1. Create the new key with the same scopes and put it in the secret store as the current version. 2. Let your workloads pick it up (a restart, or the cache TTL of your secret client). 3. Check `last_used_at` of the old key in `GET /v1/keys` stops moving. 4. Delete the old key. Webhook secret: issue a new one (`POST /v1/account/webhook_secret`). Deliveries are signed with the new and the previous secret until the next rotation, so update your receiver's secret at leisure, keeping the previous one as `previous`. Admin key: create a new admin key, store it, and delete the old one with the new one. A key cannot delete itself. ### If a key leaks 1. Disable it at once: `POST /v1/keys/{id}/disable` with your admin key. Disabling is reversible and takes effect within a minute. 2. Create a replacement and roll it out as in Rotating. 3. Read `GET /v1/usage_report` for the leaked key's line, and tell Patternode if it shows use that was not yours. 4. Delete the leaked key. If you lose every admin key, the technical contacts ask Patternode for a new delivery. That goes through the same one-time link and spoken passphrase. --- ## The mock server `mock/mock_patternode.py` is a stand-in for the Patternode API: one file, Python 3.10 or newer, standard library only. ```bash python mock/mock_patternode.py # http://127.0.0.1:4010 PATTERNODE_MOCK_PORT=5000 python mock/mock_patternode.py ``` | It does as production does | It does differently | |---|---| | Refuses a key of the wrong shape (401), a write without `x-amz-content-sha256` (400) or with the wrong hash (403) | Accepts any key of the right shape | | Refuses a test key on your own series (403 `test_key_demo_only`) | Never bills; reports a nominal `usage` | | Checks bars: order, high and low, values above zero, with `problems` listing each bad bar | Its segments are fake: every four children make a parent | | Refuses a bar before the edge (409), an edge from another series (409) and a changed edge (400) | Its edge is signed but readable; the service's is also encrypted | | Keeps nothing of your series; refuses an `Idempotency-Key` reused with another body (422) | Does not rate limit; keeps keys and jobs in memory only | | Runs jobs from links to your storage, and signs job webhooks to Standard Webhooks | Serves a stand-in for your storage at `/_bucket/`; reads CSV and JSON Lines only; signs only when `PATTERNODE_MOCK_WEBHOOK_SECRET` is set | Use it to test your code's handling of the contract, never its handling of real segments: the shapes are right, the values mean nothing. ### In your CI ```yaml - run: python mock/mock_patternode.py & - run: sleep 1 && your-test-command env: PATTERNODE_BASE_URL: http://127.0.0.1:4010 PATTERNODE_API_KEY: pnk_live_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB ``` The mock is fake, so the key in this snippet is fake too, and safe to commit. A real key never is. --- ## Quickstart Two calls: who am I, then the demonstration series. They work with a test key, which costs nothing, and with no key at all against the mock. ### 1. Choose where to call | Target | Base URL | Key | |---|---|---| | The mock, on your machine | `http://127.0.0.1:4010` | Any key of the right shape, e.g. `pnk_test_` and 36 `A`s | | Production | `https://api.patternode.com` | Your test key, from the welcome email ([Getting your API keys](api-keys.md)) | To run the mock, from the repository root (Python 3.10 or newer, nothing to install): ```bash python mock/mock_patternode.py ``` ### 2. Make the calls ```bash export PATTERNODE_BASE_URL=http://127.0.0.1:4010 export PATTERNODE_API_KEY=pnk_test_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ``` **curl** {curlQuickstart} **Python** {pyFirstCall} **TypeScript** {tsFirstCall} These are the files in the repository, embedded at build time, and CI runs each of them against the mock. What you read here is what was tested. ### 3. Read the answer A segmentation is a list of segment boxes, parents before children. Each has a level (`height`), the bars it covers, and its open, high, low and close with their times. The open right edge at each level is `provisional`. [Shards and bars](../concepts/shards.md) explains them; the [API reference](/api/patternode-api) has every field. ### Next - [Getting your API keys](api-keys.md), before you need a live key. - [Integration patterns](../guides/integration-patterns.md), to choose how your system calls Delve. - The [onboarding playbook](../playbooks/delve-onboarding.md), to plan the whole route. --- ## Production checklist PC = Production. MC = Mission Critical and Dedicated. A line marked for PC applies to MC as well. ### Credentials | | Check | Plan | |---|---|---| | ☐ | The delivered admin keys are deleted; your own are in a password manager held by both technical contacts | PC | | ☐ | Each workload and environment has its own live key (or OAuth client), narrowest scopes, expiry at most 90 days | PC | | ☐ | Keys are read from a secret store at start; none is in code, images, CI logs or tickets | PC | | ☐ | Secret scanning has the key pattern | PC | | ☐ | An IP allow list on each production key, or private connectivity only | MC | | ☐ | OAuth with `private_key_jwt` or mutual TLS, if your policy needs no long-lived secret | MC | ### Client | | Check | Plan | |---|---|---| | ☐ | Every write sends `x-amz-content-sha256` and every POST an `Idempotency-Key`, the same on each retry | PC | | ☐ | Retries: 429, 5xx and network errors, backoff with jitter, `retry-after` honoured; other 4xx never retried unchanged | PC | | ☐ | Error handling branches on `error_code`, and tolerates unknown codes, fields and event types | PC | | ☐ | Each forming series' closed segments, right edge and edge stored in one write, one writer per series; carrying on after a restart tested | PC | | ☐ | The jurisdiction endpoint is used, never a regional address | PC | | ☐ | `traceparent` sent; `server-timing` recorded | PC | | ☐ | Profile versions pinned where results must reproduce | PC | | ☐ | gRPC connections reconnect on `GOAWAY` or reset and resend what was not answered | MC | | ☐ | Callers run in two regions | MC | ### Events | | Check | Plan | |---|---|---| | ☐ | Signatures verified on the raw body; stale timestamps rejected; `id` deduplicated | PC | | ☐ | 2xx returned within 10 seconds; work done from a queue with a dead-letter queue | PC | | ☐ | Webhook secret rotation tested with both secrets live | PC | ### Data | | Check | Plan | |---|---|---| | ☐ | Series names are opaque; the mapping is in your store only | PC | | ☐ | Segments stored by client prefix; deleting a client deletes its prefix | PC | | ☐ | Jurisdiction matches where your data must stay | PC | | ☐ | Customer-managed encryption key configured, if your policy needs one | MC | ### Capacity and proof | | Check | Plan | |---|---|---| | ☐ | Peak bars a second and backfill volume within the plan, or extra capacity confirmed | PC | | ☐ | Load and soak tests passed (playbook stage 8) | PC | | ☐ | Failover rehearsal passed | MC | ### Operations | | Check | Plan | |---|---|---| | ☐ | Alerts on success rate, p99, 429 rate, edge and out-of-order errors, event lag and dead-letter depth, routed to on-call | PC | | ☐ | On-call subscribed to `status.patternode.com` | PC | | ☐ | Runbooks written and found by on-call (playbook stage 9) | PC | | ☐ | Technical contacts can open Severity 1 by phone | MC | | ☐ | Blackout periods registered | MC | | ☐ | Exit plan written | MC | --- ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | 401 `missing_api_key` on every call | Key not sent, or `Bearer` with two spaces, or the key truncated | Send `Authorization: Bearer ` with one space; check the secret store value has no newline | | 400 `missing_content_sha256` | A write without the hash | Hash the exact body bytes you send | | 403 not in the error shape | The hash does not match the body | Hash after serialising, and send those bytes unchanged; check no middleware re-encodes the body | | 403 `test_key_demo_only` | A test key on your own series | Use a live key | | 403 `terms_not_accepted` | A new account | Accept the terms through the order form or the first credit purchase | | 400 `invalid_series` | A bar out of order, high below low, a value at or below zero | Read `problems`: each names a bar and why. For values at zero, add an offset | | 409 `edge_mismatch` | The edge sent belongs to another series or profile version | Read the edge for this series from your store; keep one writer per series | | 400 `edge_invalid` | The edge was changed, truncated or re-encoded on its way through your store | Store it as an opaque string, exactly as received | | 409 `out_of_order` | A late bar, or an edge older than the bars (a second writer moved on) | Drop the bar, or read the latest edge; nothing was applied | | A failed job with `storage_unreachable` | Patternode could not read `source_url` or write `result_url`: the link expired, its signature does not cover a GET or PUT, or the bucket refused it. The job was not charged | Sign the links for at least an hour, the GET for the file and the PUT for the result, and run the job again | | 429 `rate_limited` that does not clear | Sustained traffic over your plan | Smooth to the rate in `ratelimit-policy`, or ask for capacity | | 429 `enforced_spend_limit_reached` | The plan's monthly cap | Waiting does not clear it: move up a plan | | Webhooks rejected after a rotation | The receiver holds only the new secret | Keep the previous one too, until the next rotation | | Duplicate events | Normal: delivery is at least once | Deduplicate by `id` | | Segments differ between staging and production | Different profile versions | Pin the version on both | | p99 above the service level from your side, but `server-timing` is small | The network between you and the edge | Move callers to the jurisdiction's region, or connect privately | Still stuck: open a case with the `request-id`, the time in UTC, the jurisdiction and the transport ([Support](../service/support.md)). --- ## AWS with Terraform `terraform/aws/delve-partner` creates the partner side of a Delve integration in your AWS account. Nothing in it runs at Patternode, and no secret value is in it: values are put by a person ([playbook, stage 5](../playbooks/delve-onboarding.md#stage-5-take-control-of-your-credentials)). | Resource | Is | |---|---| | `aws_kms_key` | Encrypts the secrets, the shard store and the queues | | `aws_secretsmanager_secret.live_key`, `.webhook_secret` | The key vault. The webhook secret is JSON `{"current", "previous"}` for rotation | | `aws_s3_bucket.shards` | The shard store: private, versioned, encrypted, noncurrent versions expire | | `aws_lambda_function.receiver` with a Function URL | The webhook receiver; concurrency capped at 5 | | `aws_dynamodb_table.seen` | `webhook-id`s seen, with a TTL | | `aws_sqs_queue.events`, `.dlq` | Verified events for your workers; failures land in the dead-letter queue | | `aws_iam_policy.ingest` | What your ingest workloads may do, attached to the roles you name | ### Parameters | Variable | Default | Effect | |---|---|---| | `name` | required | Prefix for every resource | | `ingest_role_arns` | `[]` | Roles that get the ingest policy | | `shard_retention_days` | 30 | Days noncurrent segment versions are kept | | `dedupe_ttl_hours` | 48 | How long a `webhook-id` is remembered | | `tags` | `{}` | Added to everything | ### Apply ```bash cd terraform/aws/delve-partner cp examples.tfvars prod.tfvars # edit terraform init terraform plan -var-file=prod.tfvars terraform apply -var-file=prod.tfvars terraform output webhook_url ``` ### Verify | Check | Expect | |---|---| | `curl -X POST "$(terraform output -raw webhook_url)" -d '{}'` | 400 `invalid`: an unsigned delivery is refused | | A test job with your staging key | A message on the events queue, the `webhook-id` in the table | | `aws s3api get-public-access-block --bucket "$(terraform output -raw shard_bucket)"` | Every setting `true` | ### Failure modes | Symptom | Cause | Fix | |---|---|---| | Every delivery 400 | Secret not put, or put as a plain string | Put `{"current": "whsec_..."}` | | Deliveries 400 after a rotation | Only the new secret stored | Store the old one as `previous` | | Messages on the dead-letter queue | Your worker fails on them | Read one, fix the worker, redrive | --- ## Backfill For bodies over 5 MB, years of history and backtests. Your file stays in your storage: you give Patternode a link to read it from and a link to write the result to, both presigned in your own bucket. Patternode reads, segments, writes and keeps nothing. ```mermaid sequenceDiagram participant I as Your ingest workload participant B as Your storage participant P as Patternode API participant W as Your event receiver I->>B: put the file; presign a GET for it and a PUT for the result I->>P: POST /v1/segmentations/jobs {series, kind, format, source_url, result_url} P-->>I: 202 job queued P->>B: GET source_url P->>B: PUT result_url (the Segmentation, as JSON) P->>W: segmentation.job.succeeded (signed) W->>B: read the result ``` | Field | Meaning | |---|---| | `source_url` | A presigned GET to the file, valid for at least an hour | | `result_url` | A presigned PUT where the result is written | | `format` | `csv`, `jsonl`, `parquet` or `arrow` | | `kind` | `bars` or `samples` | | `return_edge` | Also return the edge, to carry on as a forming series from the end of the file | | File | Layout | |---|---| | `jsonl` | One bar (or sample) a line: `[1730246400000,1730249999000,412.0,430.5,405.2,428.1]` | | `csv` | A header row, then `open_time,close_time,open,high,low,close` (or `time,value`) | | `parquet`, `arrow` | Columns `open_time, close_time, open, high, low, close` (or `time, value`), times as 64-bit integers | - One series per file. Run series in parallel up to your plan's concurrent jobs. - The job record keeps only its status and usage, never a link to your data. - A failed job is not charged; `error.details.problems` lists the bad rows. {src} --- ## Finished series ```mermaid sequenceDiagram participant I as Your ingest workload participant P as Patternode participant S as Your store I->>I: Build bars from readings I->>P: POST /v1/segmentations {series, bars} P-->>I: segments, every level I->>S: Put segments under the client's prefix ``` 1. Build bars from your readings, leaving gaps where nothing happened ([Shards and bars](../concepts/shards.md)). 2. Name the series opaquely ([Data boundary](../concepts/data-boundary.md#naming-series)). 3. Send it. Up to 5 MB of body answers at once; past that, use a [backfill](backfill.mdx). 4. Store the segments beside your readings, keyed by series and level, so a chart reads only the level it draws. 5. When more bars arrive, either send the window again (simple, and fine for daily redraws) or ask for `return_edge: true` and carry the series on as a [forming series](forming-series.mdx). Only the provisional segments can change on a resend of a longer window. Final segments keep their ids, so a store keyed on `id` updates in place. {src} --- ## Forming series :::info Preview This route works and may change with 30 days' notice. Do not build production alarms on it yet. ::: Patternode keeps nothing between calls, not even for a series that is still forming. The series carries itself: each answer returns its edge, a sealed token holding the open right edge of every level. You store the edge with the segments, and send it back with the next bars. ```mermaid sequenceDiagram participant I as Your ingest workload participant S as Your segment store participant P as Patternode I->>P: POST /v1/segmentations/extend {series, profile, bars} (no edge: a new series) P-->>I: closed, right_edge, edge I->>S: store closed (final), replace the right edge, store the edge loop each bar as it closes I->>S: read the edge I->>P: POST /v1/segmentations/extend {series, edge, bars} P-->>I: closed, right_edge, a new edge I->>S: store all three together end ``` | In the answer | Means | Do | |---|---|---| | `closed` | Segments these bars closed, now final | Store them; they never change | | `right_edge` | The open segment at each level, provisional | Replace the previous right edge with it | | `edge` | The series' state, sealed by Patternode | Store it as it is; send it back with the next bars | | `edge_last_close_time` | When the last bar closed | The next bars must open after it | | `outcome` | `started`, `extended` or `segmented` | Redraw when `segmented` | Rules: - The edge is sealed: encrypted and signed, bound to your account, the series and the profile version. It is no use to anyone else, and a changed edge is refused (400 `edge_invalid`); an edge sent for another series is refused (409 `edge_mismatch`). - Bars must open after the edge's last bar closed; otherwise 409 `out_of_order`, and nothing is applied. - The same edge with the same bars always gives the same answer. A timeout is safe to retry: resend the request you sent. - Store the closed segments, the right edge and the edge together, in one write. One writer per series: if two writers extend from the same edge, keep the first write (a version check, as [the segment store](segment-store.mdx) does). - A finished series can become a forming one: ask `POST /v1/segmentations` for `return_edge: true` and carry on from that edge. - Many series at once: `POST /v1/segmentations/extend/batch` takes up to 1,000 items, each with its own edge. Extending a series bar by bar, in any pieces, gives the same segments as sending all its bars as one finished series. {src} --- ## Integration patterns | Pattern | Use when | Calls | Status | |---|---|---|---| | [Finished series](finished-series.mdx) | You have a window of history and want every level to draw from | `POST /v1/segmentations` | Available | | [Forming series](forming-series.mdx) | Bars arrive live and you want each segment as it closes | `POST /v1/segmentations/extend`, with the edge | Preview | | [Backfill](backfill.mdx) | Years of history, or bodies over 5 MB | `POST /v1/segmentations/jobs`, from and to your storage | Available | | [Webhooks](webhooks.mdx) | You run jobs, or want account events | Your endpoint | Available | Most integrations use three: a backfill once per series, extensions from its edge from then on, and webhooks for the jobs. A reporting product that redraws daily can use finished series alone. ```mermaid flowchart TB start([A new series]) --> big{More than 5 MB of history?} big -- yes --> bf[Backfill: a job from your storage] --> wh[Webhook: job succeeded] --> store[(Store segments)] big -- no --> fin[Finished series: one call] --> store store --> live{Bars arrive live?} live -- yes --> fs[Forming series: edge plus new bars] --> store live -- no --> redraw[Redraw on a schedule: finished series again] ``` All four use the same key, client, body hash and error handling ([Requests, errors and limits](../concepts/requests.md)). --- ## Your segment store Patternode keeps none of your series, so everything about a series lives on your side: its segments at every level, and for a forming series its edge. The segment store is the reference library for that side. It runs in your process, against your storage; Patternode never sees it. | Call | Does | |---|---| | `extend(pn, series, bars)` | The whole forming-series cycle: reads the stored edge, sends it with the new bars, stores what comes back. Safe to retry | | `apply_segmentation(series, result)` | Stores a finished series' answer, and its edge if you asked for one | | `apply_extension(series, result, expected_version)` | Stores an extension: closed segments final, the right edge replaced, the new edge, in one atomic write; refuses a stale writer | | `edge(series)` | The forward edge: the sealed token, when its last bar closed, the profile version, and the store's version | | `right_edge(series)` | The open segment at each level, bottom up | | `segments(series, level, start, end)` | Segments by level and time window | | `levels(series)` | The levels a series has | | `cut(series, start, end, width_px)` | What a chart of that width should draw over that window: the finest level wide enough to read. Zoom in and finer levels take over | | `trend(series)` | Rising, falling or steady at each timescale, from the open segment at levels 1, 2 and 3 | The Python version keeps a JSON file per series in a directory, one directory per client, written atomically; replace its two storage methods with your database or object store. The TypeScript version keeps them in memory. Both are tested against the mock in CI. **Python** {py} **TypeScript** {ts} --- ## Receiving webhooks Patternode signs every delivery to the [Standard Webhooks](https://www.standardwebhooks.com) specification. The event types are on [Events](../events/index.md). 1. Set your endpoint: `PATCH /v1/account {"webhook_url": "https://..."}`. HTTPS only. 2. Get the secret: `POST /v1/account/webhook_secret`. Shown once; put it in your secret store. 3. On each delivery: 1. Verify the signature over `webhook-id.webhook-timestamp.body`, using the raw body bytes, before parsing. 2. Reject a timestamp more than 5 minutes from your clock. 3. Drop a `webhook-id` you have seen. Retries carry the same id. 4. Answer 2xx within 10 seconds, then do the work from a queue. 5. Ignore event types you do not know. During a secret rotation a delivery carries two signatures, space separated. Accept either. **Python** {py} **TypeScript** {ts} **AWS Lambda** {lambda} --- ## Overview ## Patternode for developers Everything you, or your coding agent, need to integrate with Patternode: how the products fit together from the outside, the API and event contracts, code that runs, the infrastructure on your side, and the onboarding playbook. ### The products | Product | What it is | Status | |---|---|---| | Delve | The shards toolkit. A time series in; its segments at every level out, for galleries, single views, live tiles and search. Patternode keeps none of your series. | Available. Extending forming series is in preview | | Essence | Patterns by precedent: what repeats. Runs on Delve | Planned | | Sentinel | Anomalies by shape: what moves, and the alarm on it. Runs on Delve | Planned | | Foresight | Forecasting: what comes next. Runs on Delve | Planned | ### Where to start | You want to | Read | |---|---| | Make a call in five minutes, no account needed | [Quickstart](get-started/quickstart.mdx) | | Get an account and keys | [Getting access](get-started/access.md) | | Know how you get keys | [Getting your API keys](get-started/api-keys.md) | | See the architecture: what talks to what, what you build, what data leaves you | [Architecture overview](architecture/index.md), [Data boundary](concepts/data-boundary.md) | | Design for a quant fund | [Quant fund architecture](architecture/quant/index.md) | | Plan the integration end to end | [Delve onboarding playbook](playbooks/delve-onboarding.md) | | Pick an integration pattern | [Integration patterns](guides/integration-patterns.md) | | Look up a route or an event | [API reference](/api/patternode-api), [Events](events/index.md) | | Point an agent at this | [For coding agents](resources/for-agents.md) | --- ## Finance For quantitative research, backtesting and live trading across many instruments: equities, futures, foreign exchange, crypto and rates. ### Your data, as Delve reads it | You have | Send | Notes | |---|---|---| | Trades with exchange or receive timestamps in nanoseconds | Bars built from trades, with `time_unit: "ns"` | Times are kept exactly as sent; JSON carries them as strings | | Vendor OHLC bars | The bars, as they are | Keep the vendor's bar boundaries | | Quotes | Bars of the mid, or of the bid and the ask as two series | | | A second, minute or session with no trade | Nothing: a gap | Never a flat filler bar | | Spreads or returns that cross zero | The series plus a fixed offset | Remove it when you use the segments | | Horizon | Bar width that usually fits | |---|---| | Intraday execution and signals | 1 second to 1 minute | | Swing and daily | 5 minutes to 1 hour | | Research across years | 1 day | ### The plan for the job | Use | Plan | |---|---| | Research and backtests | Production, with jobs reading Parquet or Arrow from your storage | | Live signals feeding a trading system | Mission Critical, gRPC extensions, callers in the jurisdiction's region or connected privately | | A fund at full scale, or one whose regulator treats Patternode as a critical ICT third party | Dedicated | Register your venues' trading hours as blackout periods, so Patternode changes nothing while you trade. Size capacity for the open and the close, when every instrument trades at once. ### Backtest and live agree Pin the profile version (`standard@2026-11`). The same bars with the same version give the same segments, bit for bit, on every transport, region and day, for at least 24 months. So a backtest's segments are the segments you would have had live, and a model validated on them stays valid until you choose to move version. ### Example `examples/industries/finance/trades_to_delve.py` reads invented trades for two instruments with nanosecond times, builds one-second bars (a second with no trade is a gap), backtests the day as a finished series and checks a second run is identical, then goes live from the backtest's edge, extending every instrument in one request as each second closes. The edges stay in your store. ```bash python mock/mock_patternode.py & PATTERNODE_API_KEY=pnk_live_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB python examples/industries/finance/trades_to_delve.py ``` {src} ### What you will be asked | Question | Answer | |---|---| | Tail latency? | p99 10 ms on gRPC, Mission Critical, at the edge ([Service levels](../service/service-levels.md)) | | Timestamp precision? | Nanoseconds, untouched ([Time](../service/realtime.md#time)) | | Reproducible for model validation? | Yes, per profile version ([Determinism](../service/realtime.md#determinism)) | | Change during trading hours? | None, inside your blackout periods ([Change control](../service/resilience.md#change-control)) | | DORA and outsourcing terms? | In the order form, with an exit plan ([Compliance](../service/compliance.md)) | | Does Patternode see our positions or strategies? | No: only bars under names you choose, and it keeps none of them ([Data boundary](../concepts/data-boundary.md)) | --- ## Industries Every industry uses the same API, the same events and the same service contract. An industry overlay adds what makes it quick to start in your industry: examples that read your kind of data, sample data in its shape, the bar widths and plans that usually fit, and the regulations you will be asked about. | Industry | Typical data | Overlay | |---|---|---| | [Utilities](utilities.mdx) | Historian tags, SCADA points, meter reads, inverter and transformer sensors | `examples/industries/utilities/` | | [Finance](finance.mdx) | Trades, quotes and bars, across many instruments, live and in backtest | `examples/industries/finance/` | ### Extensions If an industry ever needs something the core API does not have, it is added as an extension, never as a change to the core: an OpenAPI Overlay document in `specs/overlays/.yaml`, applied on top of `specs/openapi.yaml`. Code written against the core keeps working whether or not you use an extension. There are no extensions yet. --- ## Utilities For monitoring products and operational tools on historian, SCADA and meter data: transmission and distribution, generation fleets, water and wastewater networks, gas. ### Your data, as Delve reads it | You have | Send | Notes | |---|---|---| | A process historian's archive (PI, eDNA, Canary, a SCADA historian), compressed | Bars built from the points it kept, as kept | Do not interpolate onto a grid first: each kept point marks a change | | Points with quality codes | Only good-quality points | Map your historian's codes; a bad point is a gap, not a value | | A measure that crosses zero (net export, reactive power, temperatures below zero) | The measure plus a fixed offset | Remove the offset when you draw | | Interval meter reads (5, 15 or 30 minutes) | One bar per read interval, or per hour | An interval with no flow is a gap | | Status points (breaker open or closed, pump running) | Not Delve's input | Draw them beside the segments from your own store | | Asset view | Bar width that usually fits | |---|---| | A substation or plant over days to weeks | 15 minutes | | A feeder or inverter over hours to days | 1 to 5 minutes | | A fleet over months | 1 hour | ### The plan for the job | Use | Plan | |---|---| | An engineering or asset-management product, used in office hours | Production | | A tool in the control room, used in real-time operations | Mission Critical, in your jurisdiction, with private connectivity | | A transmission operator, or anything your regulator treats as critical outsourcing | Dedicated | Register your emergency conditions (a storm declaration, a system emergency) as blackout periods, so Patternode changes nothing while you are in one. Plan capacity for the storm, not the calm day: every point reporting at once, and backfills after an outage. ### Where it sits in your network Patternode never needs a connection into your operational network. Your systems call out from the enterprise zone or a DMZ, and events come back to a broker or endpoint you control there. That keeps Patternode outside your NERC CIP electronic security perimeter and outside the control zones of an IEC 62443 design. ```mermaid flowchart LR subgraph OT["Operational zone"] SCADA[SCADA, RTUs] --> HIST[Historian] end subgraph DMZ["DMZ"] MIRROR[Historian mirror] end subgraph IT["Enterprise zone or your cloud"] ING[Ingest workload] --> STORE[(Segments)] RCV[Event receiver] end HIST -- one-way --> MIRROR --> ING ING -- "outbound only, private connectivity" --> PN[Patternode, your jurisdiction] PN -- "events" --> RCV ``` ### Example `examples/industries/utilities/historian_to_delve.py` reads an invented historian export of a zone substation transformer (top-oil temperature and load, compressed points with quality codes). It drops bad points, builds 15-minute bars from the kept points, names each series opaquely, sends the history as finished series with a pinned profile version and its edge, and then extends every tag in one request, each from its stored edge, as each interval closes. Patternode keeps nothing. ```bash python mock/mock_patternode.py & PATTERNODE_API_KEY=pnk_live_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB python examples/industries/utilities/historian_to_delve.py ``` {src} ### What you will be asked | Question | Answer | |---|---| | Does data leave the country? | No: choose the jurisdiction ([Resilience](../service/resilience.md#jurisdictions)) | | Does Patternode connect into our OT network? | No: outbound only, as above ([Compliance](../service/compliance.md)) | | What does Patternode see? | Bars under names you made up ([Data boundary](../concepts/data-boundary.md)) | | Can an audit reproduce what operators saw? | Yes: pin the profile version ([Determinism](../service/realtime.md#determinism)) | | What happens in a regional outage? | It fails over within 60 seconds on Mission Critical, losing nothing acknowledged ([Resilience](../service/resilience.md)) | --- ## Delve onboarding playbook The supported route from first contact to Delve running in production in your environment, at the grade your use needs. Eleven stages, each ending in a gate: a check that proves the stage is done. Stages marked "agent" can be run by a coding agent working from this site; the rest need a person. The playbook is the same for every grade. What changes with the grade is the plan you choose in stage 0 and how hard stages 3, 8 and 9 bite. For a reporting product on the Production plan, stage 8 is an afternoon. For a control room or a live trading desk on Mission Critical, it is the stage that matters most. | Stage | What | Who | Gate | |---|---|---|---| | 0 | [Choose the grade and plan](#stage-0-choose-the-grade-and-plan) | You | Plan, jurisdiction, transports and contacts chosen | | 1 | [Order and receive your onboarding pack](#stage-1-order-and-receive-your-onboarding-pack) | Patternode, then you | Test key answers | | 2 | [Build against the mock](#stage-2-build-against-the-mock) | You, agent | Your tests pass against the mock | | 3 | [Design for the contract](#stage-3-design-for-the-contract) | You, agent | Capacity, latency and resilience design written down | | 4 | [Stand up your infrastructure](#stage-4-stand-up-your-infrastructure) | You, agent | Infrastructure applied in each region you run in | | 5 | [Take control of your credentials](#stage-5-take-control-of-your-credentials) | A named contact | Delivered admin key deleted; live credentials in your secret store | | 6 | [First live call](#stage-6-first-live-call) | You, agent | A real series segmented and stored | | 7 | [Wire the patterns you need](#stage-7-wire-the-patterns-you-need) | You, agent | Every flow you need runs end to end in staging | | 8 | [Prove it](#stage-8-prove-it) | You, with Patternode | Load test and failover rehearsal passed | | 9 | [Get ready to operate](#stage-9-get-ready-to-operate) | You | Monitoring, runbooks, blackout periods and exit plan in place | | 10 | [Go live](#stage-10-go-live) | You, with Patternode | Production checklist complete; hypercare running | ### Stage 0: choose the grade and plan Decide, and write down: 1. The grade. What happens to your users if Delve is unavailable for an hour? Delayed analysis is Production. A control room losing a view it operates from, or a desk unable to trade a signal, is Mission Critical. Read [Plans](../service/plans.md). 2. The jurisdiction, where your data must stay: Australia and New Zealand, the EU or the US ([Resilience](../service/resilience.md#jurisdictions)). 3. The transports. REST serves every plan and is enough for most; add gRPC for sustained high rates or tight tail latency, and your event bus for events ([Real-time](../service/realtime.md#transports)). 4. Your own service level, and the share of it you give to Patternode. If your product promises 99.95% and depends on Patternode in series, Patternode must be better than that: choose Mission Critical. 5. Two technical contacts, by name and work email. They receive the admin key and are the only people Patternode acts for on credentials. Two, so a credential can be rotated when one is away. 6. Your blackout periods (Mission Critical and Dedicated): your venues' trading hours, or the conditions under which your operator declares an emergency. 7. Your series naming scheme ([Naming series](../concepts/data-boundary.md#naming-series)). 8. Your industry's starting point: [Utilities](../industries/utilities.mdx) or [Finance](../industries/finance.mdx). Gate: a one-page integration brief with the answers to 1 to 8. ### Stage 1: order and receive your onboarding pack The order form names the plan, the jurisdiction, the transports, the technical contacts, the blackout periods and any agreed numbers. Patternode then creates your accounts (one for staging, one for production, on the same plan) and sends the onboarding pack to both technical contacts. [Getting your API keys](../get-started/api-keys.md) describes each item and how it reaches you: | Item | How it reaches you | |---|---| | Account ids, plan, jurisdiction endpoint, tier quotas, profiles and their versions | The welcome email | | A test key per account | The welcome email | | An admin key per account | A one-time link to the first contact, expiring in 72 hours; its passphrase read out on the kick-off call | | Private connectivity details (Mission Critical, Dedicated) | The welcome email: the service name to connect your endpoint to | Gate: ```bash PATTERNODE_API_KEY=pnk_test_... PATTERNODE_BASE_URL=https://us.api.patternode.com ./examples/curl/quickstart.sh ``` prints your staging account id and a demonstration segmentation, using your jurisdiction's endpoint. ### Stage 2: build against the mock ```bash python mock/mock_patternode.py ``` The mock checks what the service checks at the door (key shape, body hash, scopes, time order, the edge's seal, idempotency) and answers in the real shapes, with fake segments ([The mock server](../get-started/mock-server.md)). Copy the client you need from `examples/`, or generate one from `specs/openapi.yaml` (REST) or `specs/proto/` (gRPC), and write your tests against the mock as `examples/python/tests/test_examples.py` does. Gate: your tests pass against the mock, including: - A bad series: expect `invalid_series` with `problems`. - A retried write with the same `Idempotency-Key`: expect the first answer. - A retry with the same edge and bars: expect the same answer. A bar before the edge, an edge from another series, a changed edge: expect `out_of_order`, `edge_mismatch`, `edge_invalid`. - Your contract tests: run `tests/contract` from this repository against the mock, then against your staging account. - A 429 and a 503: expect your client to wait for `retry-after` and succeed. - A forged webhook: expect your receiver to answer 400. ### Stage 3: design for the contract Write a short design, and check it against the contract. #### Capacity | Input | Your figure | |---|---| | Series (assets times measures, or instruments times bar widths) | | | Bars a second, at peak (series divided by bar width, times the peak factor) | | | Forming series at peak | | | History to backfill (bars), and how fast | | Peak, not average. A storm can make every feeder report at once, and a market open makes every instrument trade at once. Compare the peak with your plan's figures ([Plans](../service/plans.md)). If it is over them, tell Patternode now: capacity is added within 10 business days. #### Latency budget Start from what your user sees and work back. A control room tile that must update within 2 seconds of a reading, or a signal that must reach a strategy within 50 milliseconds of a bar closing, divides into your collection, your network to Patternode, Patternode's p99 ([Service levels](../service/service-levels.md)) and your own processing and drawing. Place your callers in the same cloud region as your jurisdiction's endpoint, or connect privately, so the network share is small and steady. #### Resilience Your client MUST: - Retry 429, 5xx and network errors with backoff, honouring `retry-after`, resending the request unchanged. - Store each forming series' closed segments, right edge and edge in one write, with one writer per series ([Your segment store](../guides/segment-store.mdx)). - Reconnect gRPC streams on `GOAWAY` or reset, and resend what was not answered. - Deduplicate events by `id`. - Use the jurisdiction endpoint, never a regional address, so a regional failover is invisible. On Mission Critical, run your own callers in two regions as well. Patternode surviving a region does not help you if your ingest workload does not. Gate: the design is written down, and your peak is inside your plan or Patternode has confirmed the extra capacity. ### Stage 4: stand up your infrastructure | Building block | Why | AWS recipe | |---|---|---| | Key vault | Credentials never sit in code, images or CI logs | Secrets Manager, KMS | | Shard store | You keep the segments, beside your own data, under your client's prefix | S3, versioned, encrypted | | Event receiver | Job and account events arrive asynchronously; verify, deduplicate, queue | Lambda Function URL, DynamoDB, SQS | | Ingest workload role | The only thing that reads the live credential | IAM policy | | Private endpoint (Mission Critical, Dedicated) | Traffic to Patternode never crosses the internet | An interface VPC endpoint to the service name in your pack | ```bash cd terraform/aws/delve-partner terraform init terraform apply -var-file=staging.tfvars ``` On Mission Critical, apply it in each region your callers run in. See [Your side](../architecture/partner-side.md) for other stacks. Gate: `terraform output webhook_url` prints an HTTPS URL in each region. ### Stage 5: take control of your credentials A named contact does this, by hand, from a trusted machine, for staging and then for production. 1. Open the one-time link and enter the passphrase from the kick-off call. Put the admin key in your password manager. 2. Make your own admin key, and delete the delivered one with it: ```bash export PATTERNODE_API_KEY=pnk_admin_...delivered... python examples/python/keys.py create-admin --name ops-admin # printed once export PATTERNODE_API_KEY=pnk_admin_...yours... python examples/python/keys.py delete ``` 3. Create a live key for each workload, with the narrowest scopes and an expiry, straight into the secret store: ```bash python examples/python/keys.py create-live --name ingest --scopes full --expires-in-days 90 \ | aws secretsmanager put-secret-value --secret-id "$(terraform output -raw live_key_secret_arn)" --secret-string file:///dev/stdin ``` On Mission Critical and Dedicated you may instead register a client for OAuth with `private_key_jwt`, so your workloads hold no long-lived secret at all ([Security](../service/security.md#authentication)). 4. Issue the webhook secret, store it, and point Patternode at your receiver: ```bash python examples/python/keys.py webhook-secret \ | aws secretsmanager put-secret-value --secret-id "$(terraform output -raw webhook_secret_arn)" --secret-string file:///dev/stdin python examples/python/keys.py set-webhook "$(terraform output -raw webhook_url)" ``` 5. Add the key pattern `\bpnk_(live|test|admin)_[0-9A-Za-z]{36}\b` to your secret scanning. Gate: `python examples/python/keys.py list` shows no active key you did not create, and the delivered admin key shows `deleted`. ### Stage 6: first live call From your ingest workload in staging, with the live key read from the secret store: ```bash python examples/python/finished_series.py examples/data/export-kw.json ``` then the same with one of your own series, built into bars by your own code ([Shards and bars](../concepts/shards.md)). Pin the profile version you will run in production (`standard@2026-11`), so staging and production agree bit for bit ([Determinism](../service/realtime.md#determinism)). Gate: the segments of one real series are in your shard store, and `GET /v1/usage_report` shows the run. ### Stage 7: wire the patterns you need | Need | Pattern | |---|---| | History, galleries, reports | [Finished series](../guides/finished-series.mdx) | | Live tiles; act as a segment closes | [Forming series](../guides/forming-series.mdx), batched or on gRPC at volume | | Years of history, or a backtest | [Backfill](../guides/backfill.mdx), in Parquet or Arrow at volume | | Hear when something happens | [Webhooks](../guides/webhooks.mdx), or your event bus | Gate: each flow runs end to end in staging, including events received, verified and deduplicated, and a restart of your ingest workload that carries every forming series on from its stored edge without a gap. ### Stage 8: prove it Run these against your staging account, which is on the same plan as production. Tell Patternode the window two business days ahead so the test is not mistaken for an incident. | Test | How | Pass | |---|---|---| | Load | Your peak from stage 3, for an hour, then twice your peak for 10 minutes | Your latency budget holds at peak; at twice peak, 429s with `retry-after` and no errors your client does not absorb | | Soak | Your normal rate for 24 hours | No growth in latency, memory or open connections on your side; usage report matches your counts | | Failover rehearsal (Mission Critical, Dedicated) | Patternode drains the region serving your staging account during the window | Your users see nothing beyond your budget; no series left behind its edge; no lost or unhandled events | | Credential rotation | Rotate the live key and the webhook secret under load | No failed request, no rejected delivery | | Backfill at scale | Your largest backfill, in the format you will use | Inside your planned time; usage as estimated | Gate: every test passed, with the results written down. ### Stage 9: get ready to operate | Area | In place | |---|---| | Monitoring | Your own indicators per transport: success rate, p99 latency, 429 rate, out-of-order and edge errors, event lag, dead-letter depth. Alerts on each, routed to your on-call | | Patternode's view | `status.patternode.com` subscribed to by your on-call; `GET /v1/service_levels` in your monthly review | | Tracing | W3C `traceparent` sent on every request, so `server-timing` shows Patternode's share | | Runbooks | For: 429 storm, 5xx burst, failover, credential leak, webhook secret rotation, dead-letter redrive, catching a series up after a long outage | | Support | Technical contacts able to open Severity 1 by phone; your plan's numbers in the runbooks | | Blackout periods | Registered with Patternode (Mission Critical, Dedicated) | | Exit | Your exit plan: segments and edges are yours already; who is told | Gate: a person on call can find each runbook, and a dry run of the credential-leak runbook has been done. ### Stage 10: go live Work through the [production checklist](../go-live/production-checklist.md), switch production traffic on in steps (a tenth of your series, then half, then all, a day apart), and tell Patternode the date. On Mission Critical and Dedicated, Patternode runs two weeks of hypercare: a named engineer watching your account, and a daily check-in if you want one. Gate: every line of the checklist is ticked, all production traffic is on, and hypercare has ended without an open Severity 1 or 2. ### After go-live | Every | Do | |---|---| | Day | Watch your alerts; read the dead-letter queue | | Month | Compare `GET /v1/service_levels` and `GET /v1/usage_report` with your own figures; claim credits within 30 days if they are owed | | Quarter | Read Patternode's failover test results (Mission Critical, Dedicated); repeat your own failover rehearsal; review capacity against plan | | 90 days, or a person leaving | Rotate live keys and the webhook secret ([Rotating](../get-started/api-keys.md#rotating)) | | Release | Read the [changelog](../resources/changelog.md). Preview routes can change with 30 days' notice; pinned profile versions never change | --- ## Changelog ### Unreleased - Error `storage_unreachable`: a job failed, uncharged, because Patternode could not read its `source_url` or write its `result_url`. ### 1.0.0-draft.2, 2026-10-31 - Patternode keeps none of your series. A forming series is carried by its sealed edge: `POST /v1/segmentations/extend` and `/extend/batch` replace the stream routes (`/v1/streams`, sequences, checkpoints), and `return_edge` lets a finished series carry on. Errors `edge_invalid` and `edge_mismatch` replace `sequence_gap`. - Jobs read from and write to your own storage (`source_url`, `result_url`); `/v1/uploads` and result links are gone, and Parquet and Arrow are read. - The WebSocket subscription and the `stream.segmented` event are gone: every segmentation answers in its own response. - The segment store library for your side, in Python and TypeScript. - A contract test suite (`tests/contract`), run against the mock in CI and against any deployment with `PATTERNODE_BASE_URL`. - An Architecture section, with diagrams, for architects and engineers. ### 1.0.0-draft, 2026-10-30 - The contract published for review: REST (`specs/openapi.yaml`), events (`specs/asyncapi.yaml`), gRPC (`specs/proto/`), JSON Schemas, the service contract, and the Delve onboarding playbook. - Delve is the first product. - Profile `standard@2026-11`. - Licensed: Apache 2.0 for code and specs, CC BY 4.0 for the docs. --- ## For coding agents This site is written to be read by agents as well as people. | Resource | Where | |---|---| | An index of every page, for agents | [`/llms.txt`](pathname:///llms.txt) | | Every page in one file | [`/llms-full.txt`](pathname:///llms-full.txt) | | Any page as Markdown | The page's address with `.md` appended | | The REST contract | [`/specs/openapi.yaml`](pathname:///specs/openapi.yaml) | | The event contract | [`/specs/asyncapi.yaml`](pathname:///specs/asyncapi.yaml) | | The gRPC contract | `specs/proto/patternode/delve/v1/delve.proto` in the repository | | JSON Schemas | `specs/schemas/` in the repository | | Step-by-step instructions for an agent, and the repository's own map | [`AGENTS.md`](https://github.com/patternode/patternode-developers/blob/main/AGENTS.md) and [`llms.txt`](https://github.com/patternode/patternode-developers/blob/main/llms.txt) at the repository's root | ### Rules for an agent building an integration 1. The specs are the contract. Where a page and a spec disagree, the spec wins; report the difference. 2. Build and test against the mock (`python mock/mock_patternode.py`) before any real key is used. 3. Never write a key into code, a file, a commit, a log or a message. Read it from the environment or a secret store. Never ask a person to paste one into a chat. 4. Never call a production endpoint with a live key unless the person you work for has asked you to, in this task. 5. Copy the client from `examples/` or generate one from the specs; keep the body hash, idempotency keys and retries, and store each forming series' edge with its segments ([Your segment store](../guides/segment-store.mdx)). 6. Respect `x-status`: do not build production paths on `planned` operations. 7. Start from the industry overlay that matches your user's data: `examples/industries/`. 8. Getting real access is a person's step ([Getting access](../get-started/access.md)): tell your user to email Patternode. Do not try to obtain keys yourself. --- ## Compliance ### Assurance | Assurance | Plans | |---|---| | SOC 2 Type II report, covering security, availability and confidentiality, every year | Production and above, under NDA | | ISO/IEC 27001 certification | Production and above | | Penetration test summary, every year | Mission Critical, Dedicated | | Completed security questionnaire (CAIQ, SIG Lite) | On request | | Right to audit, with notice | Dedicated | | Subprocessor list, with 30 days' notice of change | All | | Data processing agreement | All | ### Regimes Patternode is not regulated for you; the obligations below are yours. The table says what the service gives you to meet them. | Regime | Applies to | What the service gives you | |---|---|---| | EU Digital Operational Resilience Act (DORA) | Funds and other financial entities in the EU, for ICT third-party services | Contract terms in the order form for its Article 30 (service levels, locations, access, audit, exit); incident notification within the times of [Support](support.md); resilience testing results; exit plans; subprocessor list | | UK operational resilience (PRA SS1/21, FCA SYSC 15A) and outsourcing (PRA SS2/21) | UK firms | The same terms; impact tolerances you set can rely on the published recovery objectives | | MiFID II RTS 25, SEC Rule 613 (CAT) | Trading venues and firms with clock-sync duties | Nanosecond timestamps kept exactly as sent; Patternode's own clocks synchronised to UTC within 100 microseconds | | Model risk management (SR 11-7, PRA SS1/23) | Firms using segment outputs in models | Determinism by profile version; profile versions served 24 months; a description of each profile's inputs, outputs and limits under NDA | | NERC CIP | North American bulk electric system entities | No inbound connection into your electronic security perimeter is ever needed: your systems call out, or receive events through a broker you control. Private connectivity and mutual TLS for your CIP-011 information protection. Evidence pack under NDA | | IEC 62443 | Operators of industrial automation and control systems | The same outbound-only pattern, so Patternode sits in your enterprise zone or a DMZ conduit, never in a control zone | | Australian Security of Critical Infrastructure Act, and the NZ equivalents | Critical infrastructure in Australia and New Zealand | The ANZ jurisdiction keeps data in Australia and New Zealand; ownership and offshore access disclosed | | GDPR, the NZ Privacy Act 2020, the Australian Privacy Act 1988 | Anyone sending personal data | You need not send any: series names are opaque ([Data boundary](../concepts/data-boundary.md)). A data processing agreement in any case | --- ## The service contract This section specifies the Patternode service: the plans, the service levels, how it stays up, how real-time traffic moves, how it is secured, and how it is supported. It is written for the hardest users the service is built for: | Grade | Typical user | What it demands | |---|---|---| | Utility operations | A control room or monitoring product on SCADA, historian and meter data for a grid, a water network or a generation fleet | Always on through storms and failovers; every bar accounted for; data kept in its jurisdiction; operational technology boundaries respected; audit trails for regulators | | Quantitative trading | A fund or trading desk segmenting tick and bar data across thousands of instruments, live and in backtest | Low and predictable tail latency; very high throughput; nanosecond timestamps; deterministic, reproducible results; no change during market hours | ### How to read it The key words MUST, MUST NOT, SHOULD and MAY are used as in RFC 2119 and RFC 8174. "Patternode MUST" is a commitment by Patternode; "you MUST" is a condition of the commitment. The numbers on these pages are the service's specification. They bind when your order form names your plan: the order form, and the service level agreement it incorporates, is the contract, and these pages are its technical schedule. Where an order form agrees different numbers (a Dedicated plan, for example), the order form wins. ### The pages | Page | Specifies | |---|---| | [Plans](plans.md) | The four plans, what each guarantees, and which grade each serves | | [Service levels](service-levels.md) | Availability, latency, throughput, durability, freshness and how each is measured; credits | | [Resilience](resilience.md) | Jurisdictions and regions, failover, recovery objectives, degradation, change control, testing, exit | | [Real-time](realtime.md) | Nothing kept between calls; transports (REST, gRPC, event buses), ordering, exactly-once effect, backpressure, time precision, determinism | | [Security](security.md) | Authentication, private connectivity, encryption, keys you manage, audit | | [Compliance](compliance.md) | Assurance reports, and how the service maps to the rules utilities and funds work under | | [Support](support.md) | Severities, response times, incident communication, status | | [Shared responsibility](shared-responsibility.md) | What is yours, what is Patternode's | --- ## Plans One functional contract, four plans. Every plan runs the same API: the same routes, fields, errors, events and semantics, on REST. A plan changes only how much you can send, the service levels Patternode commits to, and which faster transports are switched on for you. So code written against the Evaluation plan runs unchanged on Dedicated, and moving up a plan is a change to your order form, not to your code. ### Open for orders | Plan | State | |---|---| | Evaluation | Open | | Production | Open | | Mission Critical | By arrangement: ask, and Patternode confirms the jurisdiction and a start date | | Dedicated | By arrangement: sized and scheduled together | A plan's service levels start on the date its order form names. Until a jurisdiction is open for a plan, that plan is not sold there. ### What each plan provides | | Evaluation | Production | Mission Critical | Dedicated | |---|---|---|---|---| | For | Building and testing | Products where an outage is an inconvenience | Real-time operations and live trading | The largest utilities and funds; regulated outsourcing | | Monthly availability | None | 99.9% | 99.99% | 99.99%, or 99.995% by agreement | | Deployment | Shared | Shared, multi-zone, one region | Shared cells, active-active across two regions of your jurisdiction | Single-tenant cells, active-active across two regions, in your jurisdiction | | Transports | REST | REST, webhooks | REST, gRPC, webhooks, event buses | As Mission Critical | | Throughput (bars a second, sustained, per account) | 100 | 5,000 | 100,000 | 1,000,000 or more, sized together | | Append latency, p99, at the edge | None | 50 ms (REST) | 10 ms (gRPC), 40 ms (REST) | 5 ms (gRPC), by agreement | | Your series' data held by Patternode | None | None | None | None | | Recovery time (regional loss) | None | 4 hours | 60 seconds | 60 seconds | | Private connectivity | No | No | AWS PrivateLink, Azure Private Link, Google Private Service Connect | As Mission Critical, plus dedicated interconnect | | Customer-managed encryption keys | No | No | Yes | Yes | | Change windows | None | Announced 5 days ahead | Agreed blackout periods (market hours, storm declarations) | Agreed blackout periods, with your change board | | Support | Community | Business hours; Severity 1 in 4 hours | 24x7; Severity 1 in 15 minutes | 24x7; named engineers; Severity 1 in 15 minutes | | Service credits | No | Yes | Yes | Yes, as agreed | | Exit assistance | No | Data export | Data export, exit plan | Data export, tested exit plan, transition support | The throughput figures are what the plan is provisioned for, without asking. Patternode enforces them with the quotas every response reports in `ratelimit-policy` and `ratelimit`, so you can see your headroom before you reach it. Above them, tell Patternode 10 business days ahead and the capacity is added; the service does not refuse traffic within twice the figure while that happens, it slows it with `429` and `retry-after`. ### Which plan for which grade | Grade | Plan | Why | |---|---|---| | A monitoring product for utilities, used by engineers in office hours | Production | Outages delay analysis, not operations | | A control room tool used in real-time operations | Mission Critical | Recovery in a minute, private connectivity, blackout periods during storms | | A transmission operator, a large water utility, or anything inside a regulated outsourcing regime | Dedicated | Single tenancy, your change board, a tested exit plan | | A research team running backtests | Production, with jobs | Throughput, not tail latency | | A live trading desk | Mission Critical | gRPC, 10 ms p99, no change during market hours | | A fund at full scale, or one whose regulator treats Patternode as a critical third party | Dedicated | Sized throughput, 5 ms p99, exit plan, your audit rights | --- ## Real-time ### Nothing is kept Patternode keeps none of your series between calls: no copies, no checkpoints, no results. Every answer is complete in itself, and a forming series' state travels with you as its sealed edge ([Forming series](../guides/forming-series.mdx)). So any region can answer any call, a failover loses nothing, and there is no session to reconnect. ### Transports Every transport carries the same operations with the same semantics. REST works on every plan; the others are faster ways to do the same thing. | Transport | For | Plans | Contract | |---|---|---|---| | REST, JSON over HTTPS | Everything; the reference | All | `specs/openapi.yaml` | | Batched REST extensions | Many series, one request: up to 1,000 items, each with its own edge | All | `POST /v1/segmentations/extend/batch` | | gRPC bidirectional stream | Sustained high-rate extensions with low tail latency; one connection carries many series | Mission Critical, Dedicated | `specs/proto/patternode/delve/v1/delve.proto` | | Webhooks | Job and account events to an HTTPS endpoint | Production and above | `specs/asyncapi.yaml` | | Event buses: Amazon EventBridge, Kafka | The same events into your own bus, as CloudEvents 1.0 | Mission Critical, Dedicated | `specs/asyncapi.yaml` | | Bulk files: CSV, JSON Lines, Apache Parquet, Apache Arrow IPC | Backfills and backtests, read from and written to your storage | All | `POST /v1/segmentations/jobs` | HTTP/2 is used for REST and gRPC; keep connections open and reuse them. ### Ordering - A series' order is in its edge. The next bars must open after `edge_last_close_time`; an earlier bar is refused with 409 `out_of_order`, and nothing is applied. - One writer per series. Two writers extending from the same edge would fork it; keep the first write with a version check in your store. - Across series there is no order. ### Exactly-once effect, without server state The same edge with the same bars always gives the same answer, so a retry can never apply a bar twice: resend exactly what you sent. Store the closed segments, the right edge and the new edge in one write, and your store moves forward exactly once per answer. Other writes (keys, account settings) are applied once by `Idempotency-Key`; for 24 hours Patternode keeps the key and a hash of the request, never its data, so a retry is not billed twice. Events are delivered at least once; each has a stable `id`. ### Backpressure | Signal | Means | Do | |---|---|---| | `ratelimit` header falling | You are approaching the plan's rate | Smooth your sending | | 429 with `retry-after` | Over the rate | Wait, then resend unchanged | | gRPC flow control and `RESOURCE_EXHAUSTED` with retry info | The same, on a stream | The client library waits | ### Time Times are integers since the Unix epoch, UTC, in the unit you choose per request with `time_unit`: `ms` (the default), `us` or `ns`. In JSON, `us` and `ns` times are sent and returned as strings, because JSON numbers lose precision past 2^53; on gRPC and in Parquet and Arrow they are 64-bit integers. Segments are returned in the unit you sent. Patternode does not move your times: there is no rounding, clock adjustment or time zone. Patternode's own timestamps (`created_at`, event times, audit logs) come from clocks synchronised to UTC within 100 microseconds, and carry microseconds. ### Determinism The same bars with the same profile version give the same segments, bit for bit, on every transport, in every region, at any time. A profile is named with its version, `standard@2026-11`; a bare `standard` means the version current when you called (or, with an edge, the version sealed in it), and the answer names the version used. A profile version is never changed; new behaviour is a new version, announced in the changelog, and old versions are served for at least 24 months. Pin the version for backtests and audits. --- ## Resilience ### Jurisdictions Your data stays in the jurisdiction you choose. A jurisdiction is two cloud regions inside it, far enough apart that one event does not take both. | Jurisdiction | Endpoint | Regions | |---|---|---| | Australia and New Zealand | `anz.api.patternode.com` | Two, in Australia and New Zealand | | European Union | `eu.api.patternode.com` | Two, in the EU | | United States | `us.api.patternode.com` | Two, on the east coast and in the centre | | Any | `api.patternode.com` | Routes to your account's home jurisdiction | Patternode keeps none of your series, segments or edges at all. It MUST NOT process them outside your jurisdiction. Support staff outside it MAY see metadata (request ids, counts, error codes), never series. ### How it stays up Patternode keeps no series state, so every region and every instance can answer every call. Resilience is a matter of capacity and routing, not of moving state. | Plan | Within a region | Across regions | |---|---|---| | Production | Three zones; losing one zone is invisible to you | A second region of the jurisdiction takes the traffic within 4 hours | | Mission Critical, Dedicated | Three zones, as Production | Active-active: both regions serve your account all the time | Your traffic runs in a cell: a slice of the service with its own capacity, so another account's surge cannot use yours. Dedicated cells serve one account. ### What you see in a failover | Event | REST | gRPC stream | Webhooks and event buses | |---|---|---|---| | A zone fails | Nothing | Nothing, or a reconnect | Nothing | | A region fails (Mission Critical, Dedicated) | Requests move to the other region within 60 seconds; requests in flight may fail with 503 and are safe to resend unchanged | The connection gets `GOAWAY` or resets; reconnect and resend the requests not answered | Deliveries pause and resume from the other region; duplicates are possible | | A region is degraded but up | Patternode MAY drain it; you see the same as a failover, without errors | | | You MUST make your client follow these: resend unanswered requests unchanged, with backoff; reconnect gRPC streams; deduplicate events by id. Because your edges are in your store, nothing about your series is lost in any failure of Patternode's. The example clients do this. ### Degraded modes If a dependency fails, the service sheds what it can and keeps what matters most, in this order: 1. Extensions of forming series. 2. Finished series answered inline. 3. Event delivery. 4. Jobs. Queued jobs wait; none is lost. 5. Reports and account changes. ### Recovery objectives | | Production | Mission Critical, Dedicated | |---|---|---| | Your series' data lost in any Patternode failure | None: Patternode holds none | None | | Recovery time, region loss | 4 hours | 60 seconds | | Recovery time, loss of a jurisdiction's both regions | Best effort | 24 hours | | Account data (keys, settings, usage), recovery point | 5 minutes | 1 second | ### Change control | | Production | Mission Critical, Dedicated | |---|---|---| | Releases | Progressive: a cell at a time, watched, rolled back automatically on regression | The same, and never during your blackout periods | | Blackout periods | None | You set them: for example the trading hours of your venues, or a storm or emergency declared by your operator. Patternode MUST NOT release to your cells inside them, except to fix a Severity 1 | | Notice of change to the platform | Changelog | 5 business days, by email, for anything beyond a fix | | Engine versions | Fixed per profile version ([Determinism](realtime.md#determinism)) | The same | ### Testing Patternode MUST, every quarter, fail a region of each Mission Critical jurisdiction in production and publish the result to Mission Critical and Dedicated customers. Dedicated customers MAY take part, and MAY run their own resilience tests against their cells with 10 business days' notice. ### Exit You can leave at any time with everything that is yours: - Your segments and edges are already in your store; Patternode holds nothing of your series. - Usage and audit logs export as files. - Mission Critical and Dedicated customers get an exit plan in the order form, and Dedicated customers a tested one, with transition support for 6 months. --- ## Security ### Authentication | Method | Plans | Use when | |---|---|---| | API keys | All | The default ([Getting your API keys](../get-started/api-keys.md)) | | OAuth 2.0 client credentials, with `private_key_jwt` (RFC 7523) | Mission Critical, Dedicated | Your workloads should hold no long-lived secret: they sign a short assertion with a key that never leaves your KMS or HSM, and get an access token valid for 15 minutes | | Mutual TLS, alone or binding the token (RFC 8705) | Mission Critical, Dedicated | Your policy requires client certificates, as many operational technology and trading networks do | | Workload identity federation | Mission Critical, Dedicated | You want your cloud's own workload identity (AWS IAM, Azure Entra, Google) to stand in for a key | Whatever the method, the scopes are the same. ### Network - TLS 1.3 everywhere; TLS 1.2 with forward-secret ciphers only for clients that cannot do 1.3. - Private connectivity on Mission Critical and Dedicated: AWS PrivateLink, Azure Private Link, Google Private Service Connect, and dedicated interconnect on Dedicated. Private endpoints are the only route to your account if you ask. - An IP allow list per key, on any plan. - Webhooks come from published address ranges, and can be sent to a private endpoint. ### Encryption Data is encrypted in transit and at rest. On Mission Critical and Dedicated you MAY hold the key: Patternode keeps none of your series, so there is nothing of yours at rest to encrypt; your key seals your edges instead, so revoking it makes every edge unreadable to Patternode, which is also a way to end the service. ### Audit `GET /v1/audit_events` lists every change to your account and keys, and every sign-in and support access, with actor, time, source address and request id, kept for 7 years. It reaches your security tooling as events (`audit.*`) on Mission Critical and Dedicated. Patternode staff MUST NOT access your series. Support access to your account's metadata is logged and shown to you. ### How Patternode runs the service - Least privilege, short-lived credentials and hardware-backed multi-factor authentication for every person with production access. - Every change reviewed and deployed by pipeline; nobody changes production by hand. - Dependencies scanned, software bills of materials published for client libraries, and build provenance to SLSA level 3. - Penetration tested by an independent firm every year and after major change; Mission Critical and Dedicated customers get the summary. - Vulnerability reports to `security@patternode.com`, under the disclosure policy in `SECURITY.md`. --- ## Service levels ### Objectives | Indicator | Production | Mission Critical | Dedicated | Measured | |---|---|---|---|---| | Availability | 99.9% | 99.99% | 99.99% (99.995% by agreement) | Valid requests answered without a 5xx or a timeout, over all minutes of the calendar month, per jurisdiction | | Extension latency, REST | p99 50 ms | p50 10 ms, p99 40 ms | By agreement | From the last byte received at Patternode's edge to the first byte of the answer | | Extension latency, gRPC | | p50 2 ms, p99 10 ms, p99.9 25 ms | p99 5 ms, by agreement | From a request received to its answer on the stream, at the edge | | Finished series, inline | p99 2 s for up to 100,000 bars | p99 1 s for up to 100,000 bars | By agreement | Edge to edge | | Job start | p99 60 s | p99 15 s | p99 15 s | From 202 to `running` | | Job throughput | 10 million bars a minute per account | 50 million | Sized together | Bars processed across running jobs | | Event freshness, webhook | p99 30 s | p99 5 s | p99 5 s | From the event to the first delivery attempt | | Event delivery | At least once, retried for 24 hours | At least once, retried for 72 hours | As Mission Critical | | | Your series' data held | None: every answer is complete, and edges are yours | None | None | | Latency is measured at the edge so that it excludes the network between you and Patternode, which is yours to choose: put your caller in the same cloud region as your jurisdiction's endpoint, or use private connectivity. ### What counts as unavailable A minute is unavailable when more than 1% of valid requests in it fail with a 5xx, time out (30 seconds for REST, 5 seconds for a gRPC answer), or a gRPC connection is reset without a `GOAWAY`. These do not count: 4xx answers, including 429 within your plan's throughput; scheduled maintenance announced as the plan requires (none on Mission Critical and Dedicated, which are maintained without downtime); failures caused by your network or your cloud provider's network outside Patternode's edge; and preview or planned routes. ### Your view of the indicators - `GET /v1/service_levels` returns your account's indicators for the current and the last 12 months, per jurisdiction, as Patternode measures them. - Every response carries `request-id` and, when you send W3C `traceparent`, continues your trace, so your own monitoring sees Patternode's share of each request's time in `server-timing`. - The status page, `status.patternode.com`, shows each jurisdiction and transport, with subscriptions by email, webhook and RSS. ### Error budget and change Each month's error budget is what the availability objective allows: 43 minutes at 99.9%, 4 minutes 19 seconds at 99.99%. When a jurisdiction has spent its budget, Patternode MUST stop releasing to it anything but fixes until the budget recovers. ### Service credits | Monthly availability | Production | Mission Critical, Dedicated | |---|---|---| | Below the objective | 10% of the month's fees | 10% | | Below 99.0% (Production) or 99.9% (Mission Critical) | 25% | 25% | | Below 95.0% (Production) or 99.0% (Mission Critical) | 50% | 50% | Credits are claimed within 30 days of the month's end, by a technical contact, with the request ids or times affected. The order form may agree others. --- ## Shared responsibility | Area | Patternode | You | |---|---|---| | The service | Available, fast, correct and secure to the service levels of your plan | Choosing the plan your use needs | | Your keys | Never seeing a live key after it is created; revoking within a minute of your request | Keeping keys in a secret store; rotating them; scoping them narrowly | | Network | The edge and everything behind it | Your network to the edge; private connectivity on your side | | Your data | Keeping none of it; processing it only in your jurisdiction | Sending only what is needed; naming series opaquely; keeping the mapping | | Segments | Returning them deterministically per profile version | Storing them; drawing them; deciding what they mean for your users | | Real-time | Answering the same edge and bars with the same result; keeping nothing; delivering events at least once | Storing segments and edges in one write, one writer per series; resending unchanged on retry; deduplicating events | | Failover | Moving your traffic within the plan's recovery time | A client that resends unanswered requests and reconnects ([Resilience](resilience.md)) | | Capacity | Provisioning your plan's figures; adding more within 10 business days of notice | Telling Patternode before you go beyond them | | Change | Releasing safely, outside your blackout periods | Setting blackout periods; reading the changelog; pinning profile versions where you need to | | Incidents | Detecting, communicating and reporting as [Support](support.md) says | Watching the status page; opening Severity 1 by phone | | Compliance | The assurance in [Compliance](compliance.md) | Your own regulatory obligations | --- ## Support ### Severities | Severity | Means | |---|---| | 1 | The service is down or badly degraded in production for you, and no workaround exists | | 2 | A production function is impaired, or a workaround is burdensome | | 3 | A problem with a workaround, or a non-production environment down | | 4 | A question or a request | ### Response | Plan | Hours | Severity 1 | Severity 2 | Severity 3 | Severity 4 | Channels | |---|---|---|---|---|---|---| | Evaluation | | | | | Best effort | Community, email | | Production | Business hours in your jurisdiction | 4 hours | 8 hours | 2 business days | 5 business days | Email, portal | | Mission Critical | 24x7 for Severity 1 and 2 | 15 minutes | 1 hour | 1 business day | 2 business days | Portal, phone, shared chat channel | | Dedicated | 24x7 | 15 minutes, with a named engineer | 1 hour | 4 hours | 1 business day | As Mission Critical, plus a named technical account manager | Response is a person working on it, not an acknowledgement. Severity 1 continues around the clock until resolved. ### Incidents When Patternode has an incident affecting you: | When | Patternode MUST | |---|---| | Within 15 minutes of detection (Mission Critical, Dedicated) or 1 hour (Production) | Post it on `status.patternode.com` and notify subscribers | | Every 30 minutes until resolved | Post an update | | Within 24 hours of a major incident affecting you | Give you the initial notification details your regulator needs (DORA, NERC, national regimes) | | Within 5 business days | Publish a post-incident report: timeline, cause, impact, and the actions taken and planned | ### Your side Open Severity 1 and 2 by phone or portal, not email. Include request ids, times in UTC, the jurisdiction, and the transport. Technical contacts named in the order form can open any severity; others can open Severity 3 and 4.