Metadata-Version: 2.4
Name: originalis
Version: 0.1.1
Summary: Python Client SDK Generated by Speakeasy.
Author: Speakeasy
License: Apache-2.0
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpcore>=1.0.9
Requires-Dist: httpx>=0.28.1
Requires-Dist: pydantic>=2.11.2
Dynamic: license-file

# Originalis Python SDK

The official Python SDK for the [Originalis API](https://docs.originalis.ai) — typed, sync + async, generated from the live OpenAPI spec.

```bash
pip install originalis
```

```python
import os
from originalis import Originalis, models

with Originalis(
    security=models.Security(api_key=os.getenv("ORIGINALIS_API_KEY", "")),
) as client:
    page = client.deals.list_deals(limit=10)
```

Get an API key in the Originalis app under **Integrations → API Keys**; full guides live at [docs.originalis.ai](https://docs.originalis.ai).

[![PyPI](https://img.shields.io/pypi/v/originalis)](https://pypi.org/project/originalis/)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)

<!-- Start Summary [summary] -->
## Summary

Originalis Public API: Programmatic access to your firm's Originalis workspace — the deal pipeline, portfolio book, LP fund positions, and the network intelligence your team's own emails and calendars actually evidence.

One API key, one base URL, plain JSON. Every `GET` is a pure read; the only endpoints that change anything are the explicit `POST` **actions** (analysis, research) and webhook management — write-scoped, budgeted, and documented below.

## What you can query

| Capability | Endpoint | Typical use |
| --- | --- | --- |
| Who do we know at X | `GET /api/v1/network/who-knows` | Warm-intro sourcing: ranked paths to a company or person, each naming the teammate who owns the relationship |
| Warmth lookup | `POST /api/v1/network/warmth/lookup` | CRM enrichment: send up to 100 emails / contact ids, get relationship warmth back |
| Contact export | `GET /api/v1/network/contacts` | Sync your graph (with warmth) into a CRM or warehouse, one cursor-paginated sweep |
| Reach candidates | `GET /api/v1/network/reach/candidates` | Precomputed proxy-first-degree candidates from background fan-out runs |
| Relationship signals | `GET /api/v1/network/signals/going-stale` | Relationships drifting past their touch cadence |
| Deal pipeline | `GET /api/v1/deals` | Sync your org's deal workspace (status, stage, score) into a CRM or warehouse |
| Deal detail | `GET /api/v1/deals/{deal_id}` | Structured record for one deal: company facts, round, team, score |
| **Analyze a deal** | `POST /api/v1/deals/analyze` | Submit a company/fund website — or a DocSend/Notion/Drive/Canva/Figma/Gamma/Dropbox document link; full analysis runs async into your workspace |
| **Analyze an uploaded file** | `POST /api/v1/deals/analyze/upload` | Multipart upload a deck (PDF/PPT/DOC) and run the full analysis on it |
| **Add data-room documents** | `POST /api/v1/deals/{deal_id}/documents` | Push diligence files into a deal's data room; they're classified + analyzed async |
| Deal documents | `GET /api/v1/deals/{deal_id}/documents` | The deal's document inventory: primary uploads + data-room files with folders |
| Analysis status | `GET /api/v1/deals/{deal_id}/analysis` | Poll a submitted analysis: queued → running → succeeded/failed |
| **Analyze a founder** | `POST /api/v1/founders/analyze` | Run a founder assessment from a name / LinkedIn / GitHub; org-deduped |
| Founder analysis | `GET /api/v1/founders/analyses/{analysis_id}` | Status + the finished assessment: scores, strengths, risks, research |
| **Run research** | `POST /api/v1/research` | Submit a question; a deep-research run produces a cited Markdown report |
| Research report | `GET /api/v1/research/{research_id}` | Status + the finished report with citations |
| Portfolio book | `GET /api/v1/portfolio/companies` | Holdings with ledger economics + latest operating metrics, for warehouse sync |
| Metric history | `GET /api/v1/portfolio/companies/{company_id}/metrics` | Dated history of one metric for one holding (ARR trajectory, burn trend) |
| LP positions | `GET /api/v1/funds/positions` | Fund commitments with called/distributed/NAV, TVPI/DPI, and data-quality flags |
| LP cashflows | `GET /api/v1/funds/cashflows` | The dated call/distribution ledger, for reconciliation |
| LP mark history | `GET /api/v1/funds/marks` | Each commitment's dated NAV/TVPI trace — the momentum view |
| **Webhooks** | `POST /api/v1/webhooks` | Register a signed-event endpoint; list, delete, and inspect deliveries |

## Getting started

Three steps to a first call:

**1 — Mint a key.** In the Originalis app, go to [Integrations → API Keys](https://app.originalis.ai/app/integrations/api-keys) and create a key. The secret (`ak_...`) is shown once — store it in your secret manager. Keys can be given an expiry and revoked at any time.

**2 — Call the API.**

```bash
curl -H "Authorization: Bearer ak_..." \
  "https://api.originalis.ai/api/v1/network/who-knows?domain=acme.com"
```

**3 — Read the response.** Responses are plain JSON; list and detail
reads state their `scope` (whose data you're seeing) and carry `as_of`:

```json
{
  "target": { "domain": "acme.com", "person": null },
  "scope": "org_shared",
  "paths": [
    {
      "contact_id": "9f2c1b7a-4e11-4c2e-9b3a-1d5f6a7b8c9d",
      "name": "Jane Doe",
      "title": "CTO",
      "email": "jane@acme.com",
      "warmth": 0.72,
      "strength_score": 81.0,
      "relationship": "strong",
      "path_owner": "Mark Smith"
    }
  ],
  "unavailable_reason": null,
  "as_of": "2026-09-02T14:00:00Z"
}
```

## Authentication

Every request needs an Originalis API key, sent either way:

```bash
curl -H "Authorization: Bearer ak_..." "https://api.originalis.ai/api/v1/deals"
curl -H "X-API-Key: ak_..."            "https://api.originalis.ai/api/v1/deals"
```

Keys are bound to a user in your org; identity and org scope are resolved
**server-side** from the key — the API never accepts a client-supplied
user or organization.

## Scope: whose data comes back

Each response declares its scope explicitly:

| `scope` | Meaning | Endpoints |
| --- | --- | --- |
| `org` / `org_shared` / `org_workspace` | Your whole firm's data (network scopes pool only across members who opted into sharing) | who-knows, warmth lookup, deals, portfolio, funds |
| `key_user` | The graph of the specific user the key is bound to | contacts, reach candidates, going-stale signals |

## Shaping responses

Detail reads accept a `view` query parameter, so pollers and dashboards
aren't forced to carry full analysis bodies:

- `GET /deals/{deal_id}?view=full` — adds `analysis`: every visible
  section of the deal record with its score and summary (default view
  stays the structured facts).
- `GET /founders/analyses/{analysis_id}?view=full` — each metric adds
  `confidence`, `reasoning`, and `missing_info` alongside its score.
- `GET /research/{research_id}?view=summary` — lifecycle + executive
  summary only; the default (`full`) carries the entire Markdown report
  and citations.

Views only add or withhold optional fields — the schema of each response
is identical across views, so typed clients need no variants.

## Pagination

Three styles, stated per endpoint:

- **Offset** (`/deals`, `/portfolio/companies`): responses carry `total`,
  `limit`, `offset`, `has_more`. Sweep with `offset += limit` until
  `has_more` is `false`.
- **Cursor** (`/network/contacts`): pass each response's `next_cursor`
  back as `?cursor=` until it is `null`. Stable under concurrent writes.
- **Whole-book** (`/funds/positions`, `/funds/cashflows`,
  `/funds/marks`): LP books are small; one call returns everything.

## Errors

Errors are JSON with a human-readable `detail`:

```json
{ "detail": "Invalid or revoked API key." }
```

(Validation `422`s carry the standard structured `detail` list naming
the offending parameter.)

Responses carry an `X-Request-Id` header (the rare unhandled 500 is
the one exception). Quote it when you contact support and we can trace
the exact request in our logs.

| Code | Meaning |
| --- | --- |
| `401` | Missing, invalid, revoked, or expired key |
| `403` | Key's user belongs to no organization |
| `404` | Resource doesn't exist or isn't visible to the key's user |
| `409` | Conflict: a duplicate analysis already in flight, a webhook URL already registered, or an idempotency key from another workspace |
| `422` | Invalid parameters (the body names the parameter) |
| `429` | Daily per-key limit reached — honor `Retry-After` (seconds until UTC midnight) |
| `503` | A dependency is temporarily unavailable — retry with backoff. A failed read is **never** disguised as an empty result |

## Rate limits

Each key has a daily request budget (default 5,000/day, resets at UTC
midnight). Successful responses report where you stand:

| Header | Meaning |
| --- | --- |
| `X-RateLimit-Limit` | The key's daily budget |
| `X-RateLimit-Remaining` | Requests left today |
| `X-RateLimit-Reset` | Seconds until the budget resets (UTC midnight) |

`429` responses carry `Retry-After` (seconds). The headers are omitted
on the rare request where the counter is unreachable — they are never
guessed. Contact us if your integration needs a higher cap.

## Principles

- **Your data, your scope.** Every response is scoped to your org. Warm
  paths pool only across teammates who opted into network sharing, and
  every path names its owner.
- **Absence is honest.** Missing data comes back as `null` plus a typed
  `unavailable_reason` — never a defaulted or invented figure. Derived
  economics (TVPI, multiples) are null when their inputs are missing.
- **Reads are pure; actions are explicit.** GET endpoints never mutate
  anything or trigger background work. The only endpoints that spend —
  `POST` actions like `/deals/analyze` — require a write-scoped key,
  draw down a separate daily budget, and always return `202` with a
  poll URL.
- **Stable contract.** The `/api/v1` surface only changes additively:
  fields are added, never renamed, retyped, or removed.

## Actions (asynchronous)

Actions run Originalis analysis pipelines on demand. They differ from
reads in four deliberate ways:

- **Write-scoped key required.** Mint a key with write access; read-only
  keys get `403`. Actions are the only endpoints that can spend.
- **Separate budget.** Each key gets an actions budget (default 25/day,
  resets at UTC midnight) on top of the request limit — actions run
  real analysis pipelines with real cost. The budget is charged only
  when a run is actually dispatched: rejected, deduplicated, and
  conflicting requests cost nothing. Over-budget returns `429` with
  `Retry-After` plus `X-Actions-Limit` / `X-Actions-Remaining`.
- **Always asynchronous.** Every action returns `202` immediately with
  a `status_url`; analysis takes minutes. Poll with backoff:

```bash
DEAL=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"website_url": "https://acmerobotics.com"}' \
  "https://api.originalis.ai/api/v1/deals/analyze")
STATUS_URL="https://api.originalis.ai$(echo "$DEAL" | jq -r '.status_url')"
while :; do
  S=$(curl -s -H "X-API-Key: $KEY" "$STATUS_URL" | jq -r '.status')
  [ "$S" = "succeeded" ] || [ "$S" = "failed" ] || { sleep 30; continue; }
  break
done
curl -s -H "X-API-Key: $KEY" "https://api.originalis.ai$(echo "$DEAL" | jq -r '.result_url')"
```

- **One status vocabulary.** Every action reports
  `queued → running → succeeded | failed`, and a `succeeded` action's
  `result_url` points back into the read API — results are ordinary
  resources, never a second schema.
- **Live progress while polling.** A running action's status read
  carries a `progress` object — pipeline stages for deal analysis, the
  current stage for founder analysis, stage + percent for research — so
  a poller can show real movement, not a spinner.
- **Or stream it (SSE).** Append `/events` to an analysis resource for a
  Server-Sent Events stream of the same payloads:
  `GET /api/v1/deals/{deal_id}/analysis/events`,
  `GET /api/v1/founders/analyses/{analysis_id}/events`,
  `GET /api/v1/research/{research_id}/events`. Named events: `progress`
  (emitted on change), `done` (terminal, carries `result_url`, then the
  server closes), `error` (stream-level problem — reconnect or fall
  back to polling); keep-alive comments every 25s. Authenticate with
  the same key header (`curl -N -H "X-API-Key: $KEY" ...`). Streams are
  not resumable — on reconnect the first `progress` event re-hydrates
  you. For backends that can't hold a connection, use webhooks (below);
  polling always works.

Duplicate protection: a concurrent submission for the same target (the
same company domain in your org) returns `409` rather than silently
starting a second run.

## Webhooks

The push door: register an HTTPS endpoint and Originalis POSTs a signed
event when an action reaches a terminal state — no polling, no open
connection.

| Event | Fires when |
| --- | --- |
| `deal.analysis.completed` / `.failed` | A deal analysis finishes (or terminally fails) |
| `founder.analysis.completed` / `.failed` | A founder assessment finishes |
| `research.completed` / `.failed` | A research run finishes |

Payloads are deliberately **thin** — fetch the resource for truth:

```json
{
  "event": "deal.analysis.completed",
  "id": "9c0d1e2f-3a4b-4c5d-8e6f-7a8b9c0d1e2f",
  "created_at": "2026-09-07T15:04:05Z",
  "data": {
    "entity_id": "8a9b0c1d-2e3f-4a5b-9c6d-7e8f9a0b1c2d",
    "result_url": "/api/v1/deals/8a9b0c1d-2e3f-4a5b-9c6d-7e8f9a0b1c2d"
  }
}
```

**Verification** follows the [Standard Webhooks](https://www.standardwebhooks.com)
scheme, so off-the-shelf verifier libraries work. Each delivery carries
`webhook-id`, `webhook-timestamp`, and `webhook-signature`
(`v1,base64(HMAC-SHA256(secret, "{id}.{timestamp}.{body}"))`):

```python
import base64, hashlib, hmac

def verify(secret: str, headers: dict, body: bytes) -> bool:
    key = base64.b64decode(secret.removeprefix("whsec_"))
    message = (
        f"{headers['webhook-id']}.{headers['webhook-timestamp']}."
        + body.decode()
    )
    expected = "v1," + base64.b64encode(
        hmac.new(key, message.encode(), hashlib.sha256).digest()
    ).decode()
    return hmac.compare_digest(expected, headers["webhook-signature"])
```

**Delivery semantics**: at-least-once. Failed deliveries retry with
exponential backoff — up to 11 attempts over roughly 30 minutes. A 4xx
from your receiver stops retries immediately (except 408 and 429, which
retry like a 5xx). Deduplicate on
`webhook-id` — it is stable across retries. Answer with a 2xx within
10 seconds; do slow work after acknowledging. Endpoints must be HTTPS
on a public address; the secret is shown once at registration. Inspect
recent deliveries at `GET /api/v1/webhooks/{webhook_id}/deliveries`.

Known gap (documented, not silent): a run failed by the background
stale-timeout sweep may not produce a webhook — the poll endpoints
remain the source of truth.

## Recipes

**Sync the pipeline into a warehouse** — page until `has_more` is false:

```bash
OFFSET=0
while :; do
  PAGE=$(curl -s -H "X-API-Key: $KEY" \
    "https://api.originalis.ai/api/v1/deals?limit=50&offset=$OFFSET")
  echo "$PAGE" | jq -c '.deals[]' >> deals.ndjson
  [ "$(echo "$PAGE" | jq '.has_more')" = "true" ] || break
  OFFSET=$((OFFSET + 50))
done
```

**Enrich a CRM with warmth** — batch up to 100 identifiers per call:

```bash
curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"contacts": ["jane@acme.com", "sam@beta.io"]}' \
  "https://api.originalis.ai/api/v1/network/warmth/lookup" | jq '.results'
```

**Quarterly LP reconciliation** — positions plus the raw cashflow ledger:

```bash
curl -s -H "X-API-Key: $KEY" "https://api.originalis.ai/api/v1/funds/positions" \
  | jq '.totals'
curl -s -H "X-API-Key: $KEY" "https://api.originalis.ai/api/v1/funds/cashflows" \
  | jq '.cashflows[] | select(.cashflow_date >= "2026-07-01")'
```

**Relationship-drift alerting** — pipe going-stale signals anywhere:

```bash
curl -s -H "X-API-Key: $KEY" \
  "https://api.originalis.ai/api/v1/network/signals/going-stale?limit=10" \
  | jq -r '.signals[] | "\(.name): \(.days_since_last_touch)d since last touch"'
```

## Connect an AI agent (MCP)

Originalis is also a [Model Context Protocol](https://modelcontextprotocol.io)
server — the same API key, a different door:

```text
https://api.originalis.ai/mcp
```

- **Claude Code**:
  `claude mcp add --transport http originalis https://api.originalis.ai/mcp --header "Authorization: Bearer ak_..."`
- **Claude.ai / Claude Desktop**: add a custom connector with that URL —
  signing in with your Originalis account (OAuth) works there too. Or
  start from the in-app install page at
  [Integrations → Claude](https://app.originalis.ai/app/integrations/claude-ai).

The MCP surface is deliberately a **single conversational tool** (`ori`)
that reaches the full Originalis workspace — deal lookups, memos,
research, network questions — with real thread continuity, rather than a
zoo of per-endpoint tools.

**Which door to use:** this REST API for deterministic, typed
integrations (CRMs, warehouses, scheduled jobs — anything written in
code against a stable contract, including the async actions); MCP for
AI assistants that converse — the agent reaches the full workspace
conversationally, governed by your account's permissions.

## OpenAPI & SDKs

The machine-readable contract lives at
[`https://api.originalis.ai/api/v1/openapi.json`](https://api.originalis.ai/api/v1/openapi.json)
(OpenAPI 3.1, unauthenticated). Point any generator at it — Stainless,
Speakeasy, Fern, `openapi-generator` — to produce a typed client in your
language.

## Changelog

- **1.10.0** (2026-09-08) — document inputs: `POST /deals/analyze` accepts
  `document_url` (DocSend — with password + server-side email
  verification — Notion, Canva, Dropbox, Google Drive, Figma, Gamma);
  new `POST /deals/analyze/upload` (multipart PDF/PPT/DOC, 50 MB);
  data-room endpoints `POST`/`GET /deals/{deal_id}/documents`.
- **1.9.0** (2026-09-08) — response shaping: `?view=full` on deal and
  founder detail reads (per-section analysis; per-metric confidence +
  reasoning), `?view=summary` on research reads (lifecycle without the
  report body).
- **1.8.1** (2026-09-08) — production hardening: keys are strictly
  org-scoped on every read (cross-org rows are a plain 404); the
  actions budget is charged only when a run dispatches; per-key caps on
  concurrent event streams; webhook retries widened to ~30 minutes.
- **1.8.0** (2026-09-07) — webhooks: signed terminal-event delivery
  (Standard Webhooks conventions), endpoint management + delivery
  ledger under `/webhooks`.
- **1.7.0** (2026-09-07) — SSE streams for every action
  (`.../events`): `progress` on change, `done` with `result_url`,
  25s keep-alives.
- **1.6.0** (2026-09-07) — live `progress` on all three action status
  reads (deal pipeline stages, founder stage, research stage + percent).
- **1.5.0** (2026-09-07) — research actions: `POST /research`
  (idempotent by required key, per-user live-run gate) +
  `GET /research/{research_id}` with the cited Markdown report.
- **1.4.0** (2026-09-07) — founder analysis actions:
  `POST /founders/analyze` (org-deduped, `existing: true` on a cache
  hit) + `GET /founders/analyses/{analysis_id}`.
- **1.3.0** (2026-09-07) — first actions: `POST /deals/analyze` +
  `GET /deals/{deal_id}/analysis`. Write-scoped keys, per-key daily
  actions budget, shared async status vocabulary.
- **1.2.1** (2026-09-02) — stable `operationId` on every operation (SDK
  and agent friendly), curated examples on every response, documented
  error bodies, and `X-Request-Id` / `X-RateLimit-*` headers.
- **1.2.0** (2026-09-02) — added metric time-series
  (`/portfolio/companies/{company_id}/metrics`) and LP mark history
  (`/funds/marks`).
- **1.1.0** (2026-09-02) — added Deals (`/deals`, `/deals/{deal_id}`),
  Portfolio (`/portfolio/companies`), and Funds (`/funds/positions`,
  `/funds/cashflows`).
- **1.0.0** (2026-09-01) — initial release: Network Intelligence
  (who-knows, warmth lookup, contacts export, reach candidates,
  going-stale signals).
<!-- End Summary [summary] -->

<!-- Start Table of Contents [toc] -->
## Table of Contents
<!-- $toc-max-depth=2 -->
* [Originalis Python SDK](#originalis-python-sdk)
  * [What you can query](#what-you-can-query)
  * [Getting started](#getting-started)
  * [Authentication](#authentication)
  * [Scope: whose data comes back](#scope-whose-data-comes-back)
  * [Shaping responses](#shaping-responses)
  * [Errors](#errors)
  * [Rate limits](#rate-limits)
  * [Principles](#principles)
  * [Actions (asynchronous)](#actions-asynchronous)
  * [Webhooks](#webhooks)
  * [Recipes](#recipes)
  * [Connect an AI agent (MCP)](#connect-an-ai-agent-mcp)
  * [OpenAPI & SDKs](#openapi-sdks)
  * [Changelog](#changelog)
  * [SDK Installation](#sdk-installation)
  * [IDE Support](#ide-support)
  * [SDK Example Usage](#sdk-example-usage)
  * [Authentication](#authentication-1)
  * [Available Resources and Operations](#available-resources-and-operations)
  * [File uploads](#file-uploads)
  * [Retries](#retries)
  * [Error Handling](#error-handling)
  * [Server Selection](#server-selection)
  * [Custom HTTP Client](#custom-http-client)
  * [Resource Management](#resource-management)
  * [Debugging](#debugging)
* [Development](#development)
  * [Maturity](#maturity)
  * [Contributions](#contributions)

<!-- End Table of Contents [toc] -->

<!-- Start SDK Installation [installation] -->
## SDK Installation

> [!TIP]
> To finish publishing your SDK to PyPI you must [run your first generation action](https://www.speakeasy.com/docs/github-setup#step-by-step-guide).


> [!NOTE]
> **Python version upgrade policy**
>
> Once a Python version reaches its [official end of life date](https://devguide.python.org/versions/), a 3-month grace period is provided for users to upgrade. Following this grace period, the minimum python version supported in the SDK will be updated.

The SDK can be installed with *uv*, *pip*, or *poetry* package managers.

### uv

*uv* is a fast Python package installer and resolver, designed as a drop-in replacement for pip and pip-tools. It's recommended for its speed and modern Python tooling capabilities.

```bash
uv add git+<UNSET>.git
```

### PIP

*PIP* is the default package installer for Python, enabling easy installation and management of packages from PyPI via the command line.

```bash
pip install git+<UNSET>.git
```

### Poetry

*Poetry* is a modern tool that simplifies dependency management and package publishing by using a single `pyproject.toml` file to handle project metadata and dependencies.

```bash
poetry add git+<UNSET>.git
```

### Shell and script usage with `uv`

You can use this SDK in a Python shell with [uv](https://docs.astral.sh/uv/) and the `uvx` command that comes with it like so:

```shell
uvx --from originalis python
```

It's also possible to write a standalone Python script without needing to set up a whole project like so:

```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "originalis",
# ]
# ///

from originalis import Originalis

sdk = Originalis(
  # SDK arguments
)

# Rest of script here...
```

Once that is saved to a file, you can run it with `uv run script.py` where
`script.py` can be replaced with the actual file name.
<!-- End SDK Installation [installation] -->

<!-- Start IDE Support [idesupport] -->
## IDE Support

### PyCharm

Generally, the SDK will work well with most IDEs out of the box. However, when using PyCharm, you can enjoy much better integration with Pydantic by installing an additional plugin.

- [PyCharm Pydantic Plugin](https://docs.pydantic.dev/latest/integrations/pycharm/)
<!-- End IDE Support [idesupport] -->

<!-- Start SDK Example Usage [usage] -->
## SDK Example Usage

### Example

```python
# Synchronous Example
from originalis import Originalis, models
import os


with Originalis(
    security=models.Security(
        api_key=os.getenv("ORIGINALIS_API_KEY", ""),
    ),
) as o_client:

    res = o_client.network.who_knows(limit=10)

    # Handle response
    print(res)
```

</br>

The same SDK client can also be used to make asynchronous requests by importing asyncio.

```python
# Asynchronous Example
import asyncio
from originalis import Originalis, models
import os

async def main():

    async with Originalis(
        security=models.Security(
            api_key=os.getenv("ORIGINALIS_API_KEY", ""),
        ),
    ) as o_client:

        res = await o_client.network.who_knows_async(limit=10)

        # Handle response
        print(res)

asyncio.run(main())
```
<!-- End SDK Example Usage [usage] -->

<!-- Start Authentication [security] -->
## Authentication

### Per-Client Security Schemes

This SDK supports the following security schemes globally:

| Name             | Type   | Scheme      | Environment Variable        |
| ---------------- | ------ | ----------- | --------------------------- |
| `api_key`        | http   | HTTP Bearer | `ORIGINALIS_API_KEY`        |
| `api_key_header` | apiKey | API key     | `ORIGINALIS_API_KEY_HEADER` |

You can set the security parameters through the `security` optional parameter when initializing the SDK client instance. The selected scheme will be used by default to authenticate with the API for all operations that support it. For example:
```python
from originalis import Originalis, models
import os


with Originalis(
    security=models.Security(
        api_key=os.getenv("ORIGINALIS_API_KEY", ""),
    ),
) as o_client:

    res = o_client.network.who_knows(limit=10)

    # Handle response
    print(res)

```
<!-- End Authentication [security] -->

<!-- Start Available Resources and Operations [operations] -->
## Available Resources and Operations

<details open>
<summary>Available methods</summary>

### [Deals](docs/sdks/deals/README.md)

* [list_deals](docs/sdks/deals/README.md#list_deals) - List deals
* [get_deal](docs/sdks/deals/README.md#get_deal) - Get a deal record
* [analyze_deal](docs/sdks/deals/README.md#analyze_deal) - Analyze a company or fund
* [analyze_deal_upload](docs/sdks/deals/README.md#analyze_deal_upload) - Analyze an uploaded deck or document
* [add_deal_documents](docs/sdks/deals/README.md#add_deal_documents) - Add data-room documents to a deal
* [list_deal_documents](docs/sdks/deals/README.md#list_deal_documents) - List a deal's documents
* [get_deal_analysis](docs/sdks/deals/README.md#get_deal_analysis) - Analysis status for a deal

### [Founders](docs/sdks/founders/README.md)

* [analyze_founder](docs/sdks/founders/README.md#analyze_founder) - Analyze a founder
* [get_founder_analysis](docs/sdks/founders/README.md#get_founder_analysis) - Founder analysis status and result

### [Funds](docs/sdks/funds/README.md)

* [list_fund_positions](docs/sdks/funds/README.md#list_fund_positions) - LP fund positions
* [list_fund_cashflows](docs/sdks/funds/README.md#list_fund_cashflows) - LP cashflow ledger
* [list_fund_marks](docs/sdks/funds/README.md#list_fund_marks) - LP mark history

### [Network](docs/sdks/network/README.md)

* [who_knows](docs/sdks/network/README.md#who_knows) - Who knows this company or person
* [warmth_lookup](docs/sdks/network/README.md#warmth_lookup) - Batch warmth lookup
* [list_contacts](docs/sdks/network/README.md#list_contacts) - Export contacts with warmth
* [list_reach_candidates](docs/sdks/network/README.md#list_reach_candidates) - Reach fan-out candidates
* [list_going_stale_signals](docs/sdks/network/README.md#list_going_stale_signals) - Going-stale relationships

### [Portfolio](docs/sdks/portfolio/README.md)

* [list_portfolio_companies](docs/sdks/portfolio/README.md#list_portfolio_companies) - List portfolio holdings
* [get_metric_series](docs/sdks/portfolio/README.md#get_metric_series) - Metric history for a holding

### [Research](docs/sdks/research/README.md)

* [run_research](docs/sdks/research/README.md#run_research) - Run deep research
* [get_research](docs/sdks/research/README.md#get_research) - Research status and report

### [Webhooks](docs/sdks/webhooks/README.md)

* [list_webhooks](docs/sdks/webhooks/README.md#list_webhooks) - List webhook endpoints
* [create_webhook](docs/sdks/webhooks/README.md#create_webhook) - Register a webhook endpoint
* [delete_webhook](docs/sdks/webhooks/README.md#delete_webhook) - Delete a webhook endpoint
* [list_webhook_deliveries](docs/sdks/webhooks/README.md#list_webhook_deliveries) - Recent deliveries for an endpoint

</details>
<!-- End Available Resources and Operations [operations] -->

<!-- Start File uploads [file-upload] -->
## File uploads

Certain SDK methods accept file objects as part of a request body or multi-part request. It is possible and typically recommended to upload files as a stream rather than reading the entire contents into memory. This avoids excessive memory consumption and potentially crashing with out-of-memory errors when working with very large files. The following example demonstrates how to attach a file stream to a request.

> [!TIP]
>
> For endpoints that handle file uploads bytes arrays can also be used. However, using streams is recommended for large files.
>

```python
from originalis import Originalis, models
import os


with Originalis(
    security=models.Security(
        api_key=os.getenv("ORIGINALIS_API_KEY", ""),
    ),
) as o_client:

    res = o_client.deals.analyze_deal_upload(file={
        "file_name": "example.file",
        "content": open("example.file", "rb"),
    }, kind="company")

    # Handle response
    print(res)

```
<!-- End File uploads [file-upload] -->

<!-- Start Retries [retries] -->
## Retries

Some of the endpoints in this SDK support retries. If you use the SDK without any configuration, it will fall back to the default retry strategy provided by the API. However, the default retry strategy can be overridden on a per-operation basis, or across the entire SDK.

To change the default retry strategy for a single API call, simply provide a `RetryConfig` object to the call:
```python
from originalis import Originalis, models
from originalis.utils import BackoffStrategy, RetryConfig
import os


with Originalis(
    security=models.Security(
        api_key=os.getenv("ORIGINALIS_API_KEY", ""),
    ),
) as o_client:

    res = o_client.network.who_knows(limit=10,
        RetryConfig("backoff", BackoffStrategy(1, 50, 1.1, 100), False))

    # Handle response
    print(res)

```

If you'd like to override the default retry strategy for all operations that support retries, you can use the `retry_config` optional parameter when initializing the SDK:
```python
from originalis import Originalis, models
from originalis.utils import BackoffStrategy, RetryConfig
import os


with Originalis(
    retry_config=RetryConfig("backoff", BackoffStrategy(1, 50, 1.1, 100), False),
    security=models.Security(
        api_key=os.getenv("ORIGINALIS_API_KEY", ""),
    ),
) as o_client:

    res = o_client.network.who_knows(limit=10)

    # Handle response
    print(res)

```
<!-- End Retries [retries] -->

<!-- Start Error Handling [errors] -->
## Error Handling

[`OriginalisError`](./src/originalis/errors/originaliserror.py) is the base class for all HTTP error responses. It has the following properties:

| Property           | Type             | Description                                                                             |
| ------------------ | ---------------- | --------------------------------------------------------------------------------------- |
| `err.message`      | `str`            | Error message                                                                           |
| `err.status_code`  | `int`            | HTTP response status code eg `404`                                                      |
| `err.headers`      | `httpx.Headers`  | HTTP response headers                                                                   |
| `err.body`         | `str`            | HTTP body. Can be empty string if no body is returned.                                  |
| `err.raw_response` | `httpx.Response` | Raw HTTP response                                                                       |
| `err.data`         |                  | Optional. Some errors may contain structured data. [See Error Classes](#error-classes). |

### Example
```python
from originalis import Originalis, errors, models
import os


with Originalis(
    security=models.Security(
        api_key=os.getenv("ORIGINALIS_API_KEY", ""),
    ),
) as o_client:
    res = None
    try:

        res = o_client.network.who_knows(limit=10)

        # Handle response
        print(res)


    except errors.OriginalisError as e:
        # The base class for HTTP error responses
        print(e.message)
        print(e.status_code)
        print(e.body)
        print(e.headers)
        print(e.raw_response)

        # Depending on the method different errors may be thrown
        if isinstance(e, errors.WhoKnowsUnauthorizedError):
            print(e.data.detail)  # str
```

### Error Classes
**Primary errors:**
* [`OriginalisError`](./src/originalis/errors/originaliserror.py): The base class for HTTP error responses.
  * [`HTTPValidationError`](./src/originalis/errors/httpvalidationerror.py): Validation Error. Status code `422`. *

<details><summary>Less common errors (115)</summary>

<br />

**Network errors:**
* [`httpx.RequestError`](https://www.python-httpx.org/exceptions/#httpx.RequestError): Base class for request errors.
    * [`httpx.ConnectError`](https://www.python-httpx.org/exceptions/#httpx.ConnectError): HTTP client was unable to make a request to a server.
    * [`httpx.TimeoutException`](https://www.python-httpx.org/exceptions/#httpx.TimeoutException): HTTP request timed out.


**Inherit from [`OriginalisError`](./src/originalis/errors/originaliserror.py)**:
* [`WhoKnowsUnauthorizedError`](./src/originalis/errors/whoknowsunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`WarmthLookupUnauthorizedError`](./src/originalis/errors/warmthlookupunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListContactsUnauthorizedError`](./src/originalis/errors/listcontactsunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListReachCandidatesUnauthorizedError`](./src/originalis/errors/listreachcandidatesunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListGoingStaleSignalsUnauthorizedError`](./src/originalis/errors/listgoingstalesignalsunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListDealsUnauthorizedError`](./src/originalis/errors/listdealsunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`GetDealUnauthorizedError`](./src/originalis/errors/getdealunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealUnauthorizedError`](./src/originalis/errors/analyzedealunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealUploadUnauthorizedError`](./src/originalis/errors/analyzedealuploadunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`AddDealDocumentsUnauthorizedError`](./src/originalis/errors/adddealdocumentsunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListDealDocumentsUnauthorizedError`](./src/originalis/errors/listdealdocumentsunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`GetDealAnalysisUnauthorizedError`](./src/originalis/errors/getdealanalysisunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`AnalyzeFounderUnauthorizedError`](./src/originalis/errors/analyzefounderunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`GetFounderAnalysisUnauthorizedError`](./src/originalis/errors/getfounderanalysisunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`RunResearchUnauthorizedError`](./src/originalis/errors/runresearchunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`GetResearchUnauthorizedError`](./src/originalis/errors/getresearchunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListPortfolioCompaniesUnauthorizedError`](./src/originalis/errors/listportfoliocompaniesunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`GetMetricSeriesUnauthorizedError`](./src/originalis/errors/getmetricseriesunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListFundPositionsUnauthorizedError`](./src/originalis/errors/listfundpositionsunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListFundCashflowsUnauthorizedError`](./src/originalis/errors/listfundcashflowsunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListFundMarksUnauthorizedError`](./src/originalis/errors/listfundmarksunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListWebhooksUnauthorizedError`](./src/originalis/errors/listwebhooksunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`CreateWebhookUnauthorizedError`](./src/originalis/errors/createwebhookunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`DeleteWebhookUnauthorizedError`](./src/originalis/errors/deletewebhookunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`ListWebhookDeliveriesUnauthorizedError`](./src/originalis/errors/listwebhookdeliveriesunauthorizederror.py): Missing, invalid, revoked, or expired API key. Status code `401`. Applicable to 1 of 25 methods.*
* [`WhoKnowsForbiddenError`](./src/originalis/errors/whoknowsforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`WarmthLookupForbiddenError`](./src/originalis/errors/warmthlookupforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListContactsForbiddenError`](./src/originalis/errors/listcontactsforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListReachCandidatesForbiddenError`](./src/originalis/errors/listreachcandidatesforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListGoingStaleSignalsForbiddenError`](./src/originalis/errors/listgoingstalesignalsforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListDealsForbiddenError`](./src/originalis/errors/listdealsforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`GetDealForbiddenError`](./src/originalis/errors/getdealforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealForbiddenError`](./src/originalis/errors/analyzedealforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealUploadForbiddenError`](./src/originalis/errors/analyzedealuploadforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`AddDealDocumentsForbiddenError`](./src/originalis/errors/adddealdocumentsforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListDealDocumentsForbiddenError`](./src/originalis/errors/listdealdocumentsforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`GetDealAnalysisForbiddenError`](./src/originalis/errors/getdealanalysisforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`AnalyzeFounderForbiddenError`](./src/originalis/errors/analyzefounderforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`GetFounderAnalysisForbiddenError`](./src/originalis/errors/getfounderanalysisforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`RunResearchForbiddenError`](./src/originalis/errors/runresearchforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`GetResearchForbiddenError`](./src/originalis/errors/getresearchforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListPortfolioCompaniesForbiddenError`](./src/originalis/errors/listportfoliocompaniesforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`GetMetricSeriesForbiddenError`](./src/originalis/errors/getmetricseriesforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListFundPositionsForbiddenError`](./src/originalis/errors/listfundpositionsforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListFundCashflowsForbiddenError`](./src/originalis/errors/listfundcashflowsforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListFundMarksForbiddenError`](./src/originalis/errors/listfundmarksforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListWebhooksForbiddenError`](./src/originalis/errors/listwebhooksforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`CreateWebhookForbiddenError`](./src/originalis/errors/createwebhookforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`DeleteWebhookForbiddenError`](./src/originalis/errors/deletewebhookforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`ListWebhookDeliveriesForbiddenError`](./src/originalis/errors/listwebhookdeliveriesforbiddenerror.py): The key's user belongs to no organization. Status code `403`. Applicable to 1 of 25 methods.*
* [`GetDealNotFoundError`](./src/originalis/errors/getdealnotfounderror.py): The deal doesn't exist, isn't visible to the key's user, or has no analyzed record yet. Status code `404`. Applicable to 1 of 25 methods.*
* [`GetDealAnalysisNotFoundError`](./src/originalis/errors/getdealanalysisnotfounderror.py): The deal doesn't exist or isn't visible to the key's user. Status code `404`. Applicable to 1 of 25 methods.*
* [`GetFounderAnalysisNotFoundError`](./src/originalis/errors/getfounderanalysisnotfounderror.py): The analysis doesn't exist or isn't visible to the key's org. Status code `404`. Applicable to 1 of 25 methods.*
* [`GetResearchNotFoundError`](./src/originalis/errors/getresearchnotfounderror.py): The research doesn't exist or isn't visible to the key's org. Status code `404`. Applicable to 1 of 25 methods.*
* [`GetMetricSeriesNotFoundError`](./src/originalis/errors/getmetricseriesnotfounderror.py): No portfolio company with this id in the key's org. Status code `404`. Applicable to 1 of 25 methods.*
* [`DeleteWebhookNotFoundError`](./src/originalis/errors/deletewebhooknotfounderror.py): The webhook doesn't exist or belongs to another org. Status code `404`. Applicable to 1 of 25 methods.*
* [`ListWebhookDeliveriesNotFoundError`](./src/originalis/errors/listwebhookdeliveriesnotfounderror.py): The webhook doesn't exist or belongs to another org. Status code `404`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealConflictError`](./src/originalis/errors/analyzedealconflicterror.py): An analysis for this domain is already running in the org. Status code `409`. Applicable to 1 of 25 methods.*
* [`RunResearchConflictError`](./src/originalis/errors/runresearchconflicterror.py): The idempotency_key was already used in a different workspace. Status code `409`. Applicable to 1 of 25 methods.*
* [`CreateWebhookConflictError`](./src/originalis/errors/createwebhookconflicterror.py): URL already registered, or the per-org endpoint limit reached. Status code `409`. Applicable to 1 of 25 methods.*
* [`WhoKnowsTooManyRequestsError`](./src/originalis/errors/whoknowstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`WarmthLookupTooManyRequestsError`](./src/originalis/errors/warmthlookuptoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListContactsTooManyRequestsError`](./src/originalis/errors/listcontactstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListReachCandidatesTooManyRequestsError`](./src/originalis/errors/listreachcandidatestoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListGoingStaleSignalsTooManyRequestsError`](./src/originalis/errors/listgoingstalesignalstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListDealsTooManyRequestsError`](./src/originalis/errors/listdealstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`GetDealTooManyRequestsError`](./src/originalis/errors/getdealtoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealTooManyRequestsError`](./src/originalis/errors/analyzedealtoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealUploadTooManyRequestsError`](./src/originalis/errors/analyzedealuploadtoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`AddDealDocumentsTooManyRequestsError`](./src/originalis/errors/adddealdocumentstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListDealDocumentsTooManyRequestsError`](./src/originalis/errors/listdealdocumentstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`GetDealAnalysisTooManyRequestsError`](./src/originalis/errors/getdealanalysistoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`AnalyzeFounderTooManyRequestsError`](./src/originalis/errors/analyzefoundertoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`GetFounderAnalysisTooManyRequestsError`](./src/originalis/errors/getfounderanalysistoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`RunResearchTooManyRequestsError`](./src/originalis/errors/runresearchtoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`GetResearchTooManyRequestsError`](./src/originalis/errors/getresearchtoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListPortfolioCompaniesTooManyRequestsError`](./src/originalis/errors/listportfoliocompaniestoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`GetMetricSeriesTooManyRequestsError`](./src/originalis/errors/getmetricseriestoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListFundPositionsTooManyRequestsError`](./src/originalis/errors/listfundpositionstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListFundCashflowsTooManyRequestsError`](./src/originalis/errors/listfundcashflowstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListFundMarksTooManyRequestsError`](./src/originalis/errors/listfundmarkstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListWebhooksTooManyRequestsError`](./src/originalis/errors/listwebhookstoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`CreateWebhookTooManyRequestsError`](./src/originalis/errors/createwebhooktoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`DeleteWebhookTooManyRequestsError`](./src/originalis/errors/deletewebhooktoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`ListWebhookDeliveriesTooManyRequestsError`](./src/originalis/errors/listwebhookdeliveriestoomanyrequestserror.py): Daily per-key request budget exhausted. Honor `Retry-After` (seconds until UTC midnight). Status code `429`. Applicable to 1 of 25 methods.*
* [`WhoKnowsServiceUnavailableError`](./src/originalis/errors/whoknowsserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`WarmthLookupServiceUnavailableError`](./src/originalis/errors/warmthlookupserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListContactsServiceUnavailableError`](./src/originalis/errors/listcontactsserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListReachCandidatesServiceUnavailableError`](./src/originalis/errors/listreachcandidatesserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListGoingStaleSignalsServiceUnavailableError`](./src/originalis/errors/listgoingstalesignalsserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListDealsServiceUnavailableError`](./src/originalis/errors/listdealsserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`GetDealServiceUnavailableError`](./src/originalis/errors/getdealserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealServiceUnavailableError`](./src/originalis/errors/analyzedealserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`AnalyzeDealUploadServiceUnavailableError`](./src/originalis/errors/analyzedealuploadserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`AddDealDocumentsServiceUnavailableError`](./src/originalis/errors/adddealdocumentsserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListDealDocumentsServiceUnavailableError`](./src/originalis/errors/listdealdocumentsserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`GetDealAnalysisServiceUnavailableError`](./src/originalis/errors/getdealanalysisserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`AnalyzeFounderServiceUnavailableError`](./src/originalis/errors/analyzefounderserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`GetFounderAnalysisServiceUnavailableError`](./src/originalis/errors/getfounderanalysisserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`RunResearchServiceUnavailableError`](./src/originalis/errors/runresearchserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`GetResearchServiceUnavailableError`](./src/originalis/errors/getresearchserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListPortfolioCompaniesServiceUnavailableError`](./src/originalis/errors/listportfoliocompaniesserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`GetMetricSeriesServiceUnavailableError`](./src/originalis/errors/getmetricseriesserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListFundPositionsServiceUnavailableError`](./src/originalis/errors/listfundpositionsserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListFundCashflowsServiceUnavailableError`](./src/originalis/errors/listfundcashflowsserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListFundMarksServiceUnavailableError`](./src/originalis/errors/listfundmarksserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListWebhooksServiceUnavailableError`](./src/originalis/errors/listwebhooksserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`CreateWebhookServiceUnavailableError`](./src/originalis/errors/createwebhookserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`DeleteWebhookServiceUnavailableError`](./src/originalis/errors/deletewebhookserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ListWebhookDeliveriesServiceUnavailableError`](./src/originalis/errors/listwebhookdeliveriesserviceunavailableerror.py): A dependency is temporarily unavailable — retry with backoff. A failed read is never disguised as an empty result. Status code `503`. Applicable to 1 of 25 methods.*
* [`ResponseValidationError`](./src/originalis/errors/responsevalidationerror.py): Type mismatch between the response data and the expected Pydantic model. Provides access to the Pydantic validation error via the `cause` attribute.

</details>

\* Check [the method documentation](#available-resources-and-operations) to see if the error is applicable.
<!-- End Error Handling [errors] -->

<!-- Start Server Selection [server] -->
## Server Selection

### Override Server URL Per-Client

The default server can be overridden globally by passing a URL to the `server_url: str` optional parameter when initializing the SDK client instance. For example:
```python
from originalis import Originalis, models
import os


with Originalis(
    server_url="https://api.originalis.ai",
    security=models.Security(
        api_key=os.getenv("ORIGINALIS_API_KEY", ""),
    ),
) as o_client:

    res = o_client.network.who_knows(limit=10)

    # Handle response
    print(res)

```
<!-- End Server Selection [server] -->

<!-- Start Custom HTTP Client [http-client] -->
## Custom HTTP Client

The Python SDK makes API calls using the [httpx](https://www.python-httpx.org/) HTTP library.  In order to provide a convenient way to configure timeouts, cookies, proxies, custom headers, and other low-level configuration, you can initialize the SDK client with your own HTTP client instance.
Depending on whether you are using the sync or async version of the SDK, you can pass an instance of `HttpClient` or `AsyncHttpClient` respectively, which are Protocol's ensuring that the client has the necessary methods to make API calls.
This allows you to wrap the client with your own custom logic, such as adding custom headers, logging, or error handling, or you can just pass an instance of `httpx.Client` or `httpx.AsyncClient` directly.

For example, you could specify a header for every request that this sdk makes as follows:
```python
from originalis import Originalis
import httpx

http_client = httpx.Client(headers={"x-custom-header": "someValue"})
s = Originalis(client=http_client)
```

or you could wrap the client with your own custom logic:
```python
from originalis import Originalis
from originalis.httpclient import AsyncHttpClient
import httpx

class CustomClient(AsyncHttpClient):
    client: AsyncHttpClient

    def __init__(self, client: AsyncHttpClient):
        self.client = client

    async def send(
        self,
        request: httpx.Request,
        *,
        stream: bool = False,
        auth: Union[
            httpx._types.AuthTypes, httpx._client.UseClientDefault, None
        ] = httpx.USE_CLIENT_DEFAULT,
        follow_redirects: Union[
            bool, httpx._client.UseClientDefault
        ] = httpx.USE_CLIENT_DEFAULT,
    ) -> httpx.Response:
        request.headers["Client-Level-Header"] = "added by client"

        return await self.client.send(
            request, stream=stream, auth=auth, follow_redirects=follow_redirects
        )

    def build_request(
        self,
        method: str,
        url: httpx._types.URLTypes,
        *,
        content: Optional[httpx._types.RequestContent] = None,
        data: Optional[httpx._types.RequestData] = None,
        files: Optional[httpx._types.RequestFiles] = None,
        json: Optional[Any] = None,
        params: Optional[httpx._types.QueryParamTypes] = None,
        headers: Optional[httpx._types.HeaderTypes] = None,
        cookies: Optional[httpx._types.CookieTypes] = None,
        timeout: Union[
            httpx._types.TimeoutTypes, httpx._client.UseClientDefault
        ] = httpx.USE_CLIENT_DEFAULT,
        extensions: Optional[httpx._types.RequestExtensions] = None,
    ) -> httpx.Request:
        return self.client.build_request(
            method,
            url,
            content=content,
            data=data,
            files=files,
            json=json,
            params=params,
            headers=headers,
            cookies=cookies,
            timeout=timeout,
            extensions=extensions,
        )

s = Originalis(async_client=CustomClient(httpx.AsyncClient()))
```
### httpx2 (Pydantic's httpx fork)

[httpx2](https://httpx2.pydantic.dev/) is Pydantic's maintained fork of `httpx`. To run this SDK on httpx2, call `alias_httpx()` at your program's entry point, before importing the SDK, so every `import httpx` — including the ones inside the SDK — resolves to `httpx2`:
```python
import httpx2

httpx2.alias_httpx()

from originalis import Originalis

s = Originalis()
```

An SDK can also be generated against httpx2 directly, so it depends on the fork instead of `httpx`, by setting `python.httpClientLibrary: httpx2` in `gen.yaml`.
<!-- End Custom HTTP Client [http-client] -->

<!-- Start Resource Management [resource-management] -->
## Resource Management

The `Originalis` class implements the context manager protocol and registers a finalizer function to close the underlying sync and async HTTPX clients it uses under the hood. This will close HTTP connections, release memory and free up other resources held by the SDK. In short-lived Python programs and notebooks that make a few SDK method calls, resource management may not be a concern. However, in longer-lived programs, it is beneficial to create a single SDK instance via a [context manager][context-manager] and reuse it across the application.

[context-manager]: https://docs.python.org/3/reference/datamodel.html#context-managers

```python
from originalis import Originalis, models
import os
def main():

    with Originalis(
        security=models.Security(
            api_key=os.getenv("ORIGINALIS_API_KEY", ""),
        ),
    ) as o_client:
        # Rest of application here...


# Or when using async:
async def amain():

    async with Originalis(
        security=models.Security(
            api_key=os.getenv("ORIGINALIS_API_KEY", ""),
        ),
    ) as o_client:
        # Rest of application here...
```
<!-- End Resource Management [resource-management] -->

<!-- Start Debugging [debug] -->
## Debugging

You can setup your SDK to emit debug logs for SDK requests and responses.

You can pass your own logger class directly into your SDK.
```python
from originalis import Originalis
import logging

logging.basicConfig(level=logging.DEBUG)
s = Originalis(debug_logger=logging.getLogger("originalis"))
```

You can also enable a default debug logger by setting an environment variable `ORIGINALIS_DEBUG` to true.
<!-- End Debugging [debug] -->

<!-- Placeholder for Future Speakeasy SDK Sections -->

# Development

## Maturity

This SDK is in beta, and there may be breaking changes between versions without a major version update. Therefore, we recommend pinning usage
to a specific package version. This way, you can install the same version each time without breaking changes unless you are intentionally
looking for the latest version.

## Contributions

While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation. 
We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release. 

### SDK Created by [Speakeasy](https://www.speakeasy.com/?utm_source=originalis&utm_campaign=python)
