Skip to content

Latest commit

 

History

History
8181 lines (5679 loc) · 135 KB

File metadata and controls

8181 lines (5679 loc) · 135 KB

Reference

auth

client.auth.whoami() -> ApiResponseAuthWhoamiOut

📝 Description

Return the resolved authentication context for the current credential.

Useful for verifying that a Bearer JWT or API key is valid and discovering which workspace and permission scopes it grants — call this first when debugging authentication issues or bootstrapping an SDK integration.

The auth_kind field indicates whether the credential is a session token (clerk) or a programmatic key (api_key). For API keys, workspace_id and api_key_id are always populated; for session tokens, workspace_id reflects the X-Workspace-Id header value (if present) and api_key_id is null. The scopes list is sorted and deduplicated.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.auth.whoami()

⚙️ Parameters

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

dictionary

client.dictionary.list_dictionary_entries(...) -> ApiListResponseDictionaryOut

📝 Description

List dictionary entries for a single locale in the current workspace.

Returns a paginated list of entries for the BCP-47 language locale specified via the ?language= query parameter (required, e.g. ko-kr). Use GET /dictionary/search instead when you need to match by word text across multiple locales, or GET /dictionary/languages to discover which locales have entries before filtering here.

audio_url on entries with method=recorded is a short-lived presigned URL — do not cache it across sessions.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.dictionary.list_dictionary_entries(
    language="de-de",
)

⚙️ Parameters

language: ListDictionaryEntriesApiV1DictionaryGetRequestLanguage — BCP-47 language code, e.g. en-us, ko-kr

method: typing.Optional[typing.List[DictionaryMethod]] — Filter by one or more entry methods. Repeat to OR: ?method=spelled&method=recorded. Omit to return all methods.

sort: typing.Optional[ListDictionaryEntriesApiV1DictionaryGetRequestSort] — Field to sort by. uses_count ranks the most-applied entries first, useful for auditing high-impact corrections.

order: typing.Optional[ListDictionaryEntriesApiV1DictionaryGetRequestOrder] — Sort direction.

offset: typing.Optional[int] — Zero-based pagination offset.

limit: typing.Optional[int] — Page size (max 50).

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.dictionary.create_dictionary_entry(...) -> ApiResponseDictionaryOut

📝 Description

Create a pronunciation dictionary entry in the current workspace.

Dictionary entries teach the synthesis pipeline how to pronounce words that it would otherwise handle incorrectly — brand names, acronyms, technical terms, proper nouns, and foreign loanwords. Each entry is scoped to a single BCP-47 locale and is applied during workflow execution when that locale is the synthesis target.

Three methods are supported via the method field:

  • spelled — provide a phonetic respelling in pronunciation (e.g. "Poh-doh-nohs"). pronunciation is required for this method.
  • recorded — attach a reference audio clip by supplying an upload_id from a completed /uploads staging upload with category dictionary. The audio is copied to permanent storage on create; the upload slot is consumed and cannot be reused for a different entry.
  • ipa — supply an IPA transcription in ipa. pronunciation is optional as a human-readable gloss alongside the IPA.

Returns 409 if a (word, language) pair already exists in the workspace. Requires editor workspace role and the dictionary:write scope.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.dictionary.create_dictionary_entry(
    word="word",
    method="spelled",
    language="language",
)

⚙️ Parameters

word: str — The surface form of the word or phrase as it appears in a script.

method: DictionaryMethod — Pronunciation method: spelled (phonetic respelling), recorded (reference audio clip), or ipa (IPA transcription).

language: str — BCP-47 locale this entry applies to (e.g. ko-kr). Case-insensitive; stored lowercase.

workspace_id: typing.Optional[str]

description: typing.Optional[str] — Optional human-readable note about the entry (e.g. context, source).

pronunciation: typing.Optional[str] — Phonetic respelling. Required when method is spelled.

upload_id: typing.Optional[str] — ID of a completed staging upload (category dictionary). Required when method is recorded; consumed on create.

ipa: typing.Optional[str] — IPA transcription of the word. Supplied by the caller; automatic generation is a planned enhancement.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.dictionary.search_dictionary_entries(...) -> ApiListResponseDictionaryOut

📝 Description

Search dictionary entries by word text across one or more locales.

Performs a case-insensitive substring match on the word field. Optionally narrow to one or more BCP-47 locales by repeating ?language= (OR logic). Omitting language searches across all locales in the workspace.

Use GET /dictionary (locale-scoped list) when you want the full entry list for a specific locale; use this endpoint when you need to find how a word is defined across languages or when the user is typing a search query.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.dictionary.search_dictionary_entries(
    search="search",
)

⚙️ Parameters

search: str — Substring to match against the word field (case-insensitive).

sort: typing.Optional[SearchDictionaryEntriesApiV1DictionarySearchGetRequestSort] — Field to sort by.

order: typing.Optional[SearchDictionaryEntriesApiV1DictionarySearchGetRequestOrder] — Sort direction.

offset: typing.Optional[int] — Zero-based pagination offset.

limit: typing.Optional[int] — Page size (max 50).

language: typing.Optional[typing.List[SearchDictionaryEntriesApiV1DictionarySearchGetRequestLanguageItem]] — Repeat for OR, e.g. ?language=en-us&language=ko-kr

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.dictionary.list_dictionary_languages(...) -> ApiResponseListDictionaryLanguageOut

📝 Description

Return all locales that have at least one dictionary entry, with entry counts.

Results are ordered by entry count descending, then BCP-47 locale code ascending. Use this endpoint to populate a locale filter dropdown before calling GET /dictionary?language=, rather than hard-coding the supported locale list in your client.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.dictionary.list_dictionary_languages()

⚙️ Parameters

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.dictionary.suggest_pronunciation(...) -> ApiResponsePronunciationSuggestion

📝 Description

Generate a pronunciation suggestion for a word before saving it as a dictionary entry.

Returns a pronunciation string suitable for use as the pronunciation field when creating a spelled-method dictionary entry. The suggestion is deterministic (same word always returns the same result) and is intended as a starting point for human review, not as a production-ready transcription.

language is accepted to maintain a consistent request shape for future per-locale phonetic rules; it does not affect the current output. ipa is always null in this version — automatic IPA generation is a planned enhancement.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.dictionary.suggest_pronunciation(
    word="word",
    language="language",
)

⚙️ Parameters

word: str — The word or phrase to generate a pronunciation suggestion for.

language: str — BCP-47 locale of the word. Reserved for future per-locale phonetic rules; does not affect current output.

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.dictionary.update_dictionary_entry(...) -> ApiResponseDictionaryOut

📝 Description

Update fields on an existing dictionary entry in the current workspace.

Supports partial updates — only the fields included in the request body are changed; omitted fields retain their current values. Passing null for word, method, or language is rejected with 422, as these fields are required on the stored entry.

To replace the reference audio on a recorded-method entry, supply a new upload_id pointing to a completed staging upload. The previous audio is orphaned (not deleted from storage) and the new file is copied to permanent storage atomically.

Returns 409 if the new (word, language) combination already exists in the workspace. Requires editor workspace role and the dictionary:write scope.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.dictionary.update_dictionary_entry(
    entry_id="entry_id",
)

⚙️ Parameters

entry_id: str

workspace_id: typing.Optional[str]

word: typing.Optional[str] — Updated surface form. Omit to leave unchanged.

description: typing.Optional[str] — Updated human-readable note. Omit to leave unchanged.

pronunciation: typing.Optional[str] — Updated phonetic respelling. Required when changing method to spelled.

upload_id: typing.Optional[str] — New staging upload ID to replace the reference audio. Required when changing method to recorded.

method: typing.Optional[DictionaryMethod] — Updated pronunciation method. Omit to leave unchanged.

language: typing.Optional[str] — Updated BCP-47 locale. Omit to leave unchanged.

ipa: typing.Optional[str] — Updated IPA transcription. Omit to leave unchanged; supply null explicitly to clear.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.dictionary.delete_dictionary_entry(...) -> ApiResponseDict

📝 Description

Delete a dictionary entry from the current workspace.

The entry is removed from the workspace's dictionary and will no longer influence synthesis output in subsequent workflow runs. The operation is not reversible via the API — create a new entry to restore the pronunciation. Returns an empty data object on success. Requires editor workspace role and the dictionary:write scope.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.dictionary.delete_dictionary_entry(
    entry_id="entry_id",
)

⚙️ Parameters

entry_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

nodes

client.nodes.list_nodes() -> ApiListResponseNodePortsOut

📝 Description

List all available node types with their input/output port schemas.

Returns the static structural definition for every node type registered in the catalog — what ports each node exposes, their names, and expected data shapes — without runtime-variable values such as available languages or the TTS model catalog. This endpoint requires no X-Workspace-Id header and no authentication, making it suitable for static documentation generation and canvas layout tooling.

For the full runtime configuration options a user would pick when wiring up a specific node (available target languages, provider/model options, voice picker URL), use GET /api/v2/nodes/{node_type} instead.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.nodes.list_nodes()

⚙️ Parameters

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.nodes.get_node_detail(...) -> ApiResponseNodeDetailOut

📝 Description

Return full node definition and runtime configuration options for a node type.

Deprecated: use GET /api/v2/nodes/{node_type} instead. This v1 variant inlines the full TTS model catalog under options.models_by_provider, which creates a large response and couples clients to the catalog structure. The v2 endpoint replaces that inline tree with a providers HATEOAS href pointing to the standalone /api/v1/providers catalog, so the model list is fetched lazily only when needed.

Unlike GET /nodes (which returns only static port schemas), this endpoint returns the runtime values a caller uses to configure a node: supported target languages derived from deployment settings, the available model catalog, and a HATEOAS link to the workspace-scoped voice list. Requires X-Workspace-Id.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.nodes.get_node_detail(
    node_type="node_type",
)

⚙️ Parameters

node_type: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.nodes.get_node_detail_v2(...) -> ApiResponseNodeDetailOut

📝 Description

Return full node definition and runtime configuration options for a node type (v2).

Extends GET /api/v1/nodes/{node_type} by replacing the large inline model catalog (options.models_by_provider) with a providers HATEOAS href pointing to GET /api/v1/providers. Clients follow that link to load the model list and each model's configuration schema only when the user opens the relevant configuration panel, rather than receiving it in every node-detail response.

The voices HATEOAS href (with its provider, model, and language filter parameters) is unchanged from v1, so the voice picker does not require a catalog call. Supported target languages are resolved from deployment settings at request time. Requires X-Workspace-Id and the catalog:read scope.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.nodes.get_node_detail_v2(
    node_type="node_type",
)

⚙️ Parameters

node_type: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

providers

client.providers.list_catalog_providers(...) -> ApiListResponseCatalogProviderOut

📝 Description

List all available speech synthesis providers in the catalog.

Returns the full set of processing providers — each with its display name, number of available models, and a HATEOAS models link to GET /providers/{provider}/models. The response contains only customer-facing metadata; cost, credentials, and base URLs are never included.

This endpoint is the starting point for building a provider/model/voice selection flow. The typical traversal is: list providers → follow models link → follow voices link for the chosen model. Requires X-Workspace-Id and the catalog:read scope.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.providers.list_catalog_providers()

⚙️ Parameters

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.providers.get_catalog_provider(...) -> ApiResponseCatalogProviderOut

📝 Description

Get a single speech synthesis provider by its canonical identifier.

Returns the same shape as an item in GET /providers — display name, model count, and a HATEOAS models link — but scoped to a single provider. Returns 404 if the provider identifier is not recognized. The canonical identifier is the lowercase slug returned in the provider field of the list response.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.providers.get_catalog_provider(
    provider="provider",
)

⚙️ Parameters

provider: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.providers.list_catalog_provider_models(...) -> ApiListResponseCatalogModelOut

📝 Description

List all models available for a given provider.

Returns each model's display name, content type, live voice_count (the number of platform voices catalogued under that model), and a controls map describing the canonical provider-agnostic parameters supported by the model (e.g. speed, stability). Also includes config_schema for back-compat — new integrations should prefer controls as the authoritative parameter description. Each item includes a HATEOAS voices link to the paginated voice list for that model. Returns 404 if the provider is not recognized.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.providers.list_catalog_provider_models(
    provider="provider",
)

⚙️ Parameters

provider: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.providers.list_catalog_provider_model_voices(...) -> ApiCountedListResponseCatalogVoiceOut

📝 Description

List platform voices available for a specific provider and model.

Returns a paginated list of voices from the platform catalog (system workspace) that declare support for the given model. A voice can appear under multiple models when its supported_models list includes more than one entry; voices with no supported models are excluded from all model listings.

Each voice includes gender, age, accent, supported locales, and a short-lived presigned preview_url for the audio sample — do not cache these URLs across sessions. The response pagination.total field reflects the total match count for the provider/model pair.

For the workspace voice picker (which merges platform and workspace-scoped voices and supports favorite/similarity filtering), use GET /voices with ?provider= and ?model= query parameters instead.

Returns 404 if the provider or model is not recognized.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.providers.list_catalog_provider_model_voices(
    provider="provider",
    model="model",
)

⚙️ Parameters

provider: str

model: str

offset: typing.Optional[int] — Zero-based pagination offset.

limit: typing.Optional[int] — Page size (max 100).

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.providers.get_catalog_provider_model(...) -> ApiResponseCatalogModelOut

📝 Description

Get a single model for a given provider.

Returns the same shape as an item in GET /providers/{provider}/models, including controls (canonical parameter map), config_schema (for back-compat), live voice_count, and a HATEOAS voices link. Returns 404 if the provider or model identifier is not recognized.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.providers.get_catalog_provider_model(
    provider="provider",
    model="model",
)

⚙️ Parameters

provider: str

model: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

templates

client.templates.list(...) -> ApiListResponseTemplateOut

📝 Description

Browse the public template gallery across all workspaces.

Returns only templates that have an active published snapshot (is_public=true, published_definition set, not unpublished). Results come from any workspace — the gallery is intentionally cross-workspace so callers can discover shared starting points regardless of their own workspace membership.

Does not require X-Workspace-Id, so callers without a workspace (e.g. during onboarding) can still browse. The response reflects the published snapshot for each row, not any unpublished draft edits.

Dual-auth: Bearer JWT or API key (scope templates:read).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.list()

⚙️ Parameters

category: typing.Optional[typing.List[TemplateCategory]] — Filter by category. Repeat the parameter for OR logic, e.g. ?category=media&category=creative.

search: typing.Optional[str] — Full-text search over template name and description.

sort: typing.Optional[ListTemplatesRequestSort] — Sort order: recent (last published), popular (most cloned), or name (alphabetical).

offset: typing.Optional[int] — Zero-based offset for page navigation.

limit: typing.Optional[int] — Maximum number of templates to return (1–100).

favorites_only: typing.Optional[bool]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.templates.create_template(...) -> ApiResponseTemplateOut

📝 Description

Create a reusable workflow template in the current workspace.

Templates are workspace-private on creation (is_public=false, is_starter=false). The full WorkflowDefinition (graph + execution config) is validated at write time — structural errors (duplicate node/edge IDs, port mismatches, etc.) surface here rather than when a caller later clones the template into a workflow.

Use this to capture a workflow configuration you intend to reuse or share. To make a template available in the public gallery, an admin must mark it public via the admin API. To create a runnable workflow from an existing template, use POST /templates/{id}/clone instead.

Requires workspace editor role or higher.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.create_template(
    name="name",
)

⚙️ Parameters

name: str — Display name for the template (1–200 characters, not blank).

workspace_id: typing.Optional[str]

description: typing.Optional[str] — Optional human-readable description shown in the gallery (max 2,000 characters).

category: typing.Optional[TemplateCategory] — Optional category tag used for gallery filtering.

definition: typing.Optional[WorkflowDefinitionInput] — Full workflow definition (graph + execution config). Validated at write time — structural errors are rejected with 422.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.templates.get(...) -> ApiResponseTemplateOut

📝 Description

Fetch a single template by ID.

Returns the template if it is visible to the caller: templates owned by the caller's workspace are returned with the live draft definition; public/starter templates from other workspaces are returned with the published snapshot.

Returns 404 for templates that exist but are not visible to the caller (not owned, not public, not a starter) — same response as for a missing ID.

Dual-auth: Bearer JWT or API key (scope templates:read).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.get(
    template_id="template_id",
)

⚙️ Parameters

template_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.templates.delete_template(...) -> ApiResponseDict

📝 Description

Delete a template owned by the caller's workspace.

The delete is a soft delete — the record is hidden from the gallery and all visibility checks immediately, but is not physically removed. Any workflows previously cloned from this template are unaffected; clone creates an independent copy of the definition at clone time.

Restrictions:

  • Only the owning workspace may delete its templates (403 otherwise).
  • Platform starter templates (is_starter=true) cannot be deleted (403).

Requires workspace editor role or higher.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.delete_template(
    template_id="template_id",
)

⚙️ Parameters

template_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.templates.update_template(...) -> ApiResponseTemplateOut

📝 Description

Update a template owned by the caller's workspace.

All fields are optional (omit to keep the stored value). When definition is supplied it is a full replace — send the complete graph, not a partial diff. Structural validation runs on write, same as POST /templates.

Restrictions:

  • Only the owning workspace may update its templates (403 otherwise).
  • Platform starter templates (is_starter=true) are read-only via this endpoint regardless of workspace ownership (403).
  • Updates apply only to the draft/live definition; the published gallery snapshot is not updated until an admin republishes.

Requires workspace editor role or higher.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.update_template(
    template_id="template_id",
)

⚙️ Parameters

template_id: str

workspace_id: typing.Optional[str]

name: typing.Optional[str]

description: typing.Optional[str]

category: typing.Optional[TemplateCategory]

definition: typing.Optional[WorkflowDefinitionInput] — Full-replace on PATCH. Omit to keep the stored value. Explicit null is rejected — there is no 'empty graph' use-case worth the ambiguity. The union with null here only makes omission easy for FE clients; see reject_null_definition for the runtime guard.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.templates.estimate_template(...) -> ApiResponseTemplateEstimateResponse

📝 Description

Estimate the credit cost of running a workflow built from this template.

Returns a per-unit pricing guide expressed in credits per unit_chars input characters (default 1,000). Because the template does not contain the caller's actual script, the estimate uses a synthetic fixed-length input to compute a reproducible per-unit rate. Multiply by your expected character count to project total cost.

The response distinguishes variable costs (scale with script length, e.g. synthesis) from fixed costs (apply once per run regardless of length). A node-level breakdown is included so callers can see which processing steps drive the cost.

Results are cached against the template definition and current pricing rates. cache_status indicates whether this response was served from cache (hit), computed fresh (miss), or recomputed because the definition or rates changed (stale).

Visibility rules match GET /templates/{id} — own-workspace templates use the draft definition; cross-workspace templates use the published snapshot.

Dual-auth: Bearer JWT or API key (scope templates:read).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.estimate_template(
    template_id="template_id",
)

⚙️ Parameters

template_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.templates.clone(...) -> ApiResponseWorkflowOut

📝 Description

Create a runnable workflow in the caller's workspace from a template.

This is the primary way to use a template: it produces a new Workflow owned by the caller's workspace, ready to accept scripts and run jobs.

Use body.name to set the workflow name; omit it (or send blank/whitespace) to get the default "{template name} (Copy)".

Cross-workspace clones (gallery/starter templates) copy the published snapshot so unpublished draft edits made by the template owner never leak to other workspaces. Same-workspace clones copy the live draft definition.

Use GET /templates/{id}/estimate first to preview credit costs before committing to a clone and run. Use POST /workflows/{id}/duplicate to copy an existing workflow rather than starting from a template.

Requires workspace editor role or higher. Dual-auth: Bearer JWT or API key (scope workflows:write).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.clone(
    template_id="template_id",
)

⚙️ Parameters

template_id: str

workspace_id: typing.Optional[str]

name: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.templates.favorite_template(...) -> ApiResponseTemplateOut

📝 Description

Add a template to the current user's favorites.

Favorites are per-user, not per-workspace — the same favorite list is visible regardless of which workspace the caller is currently acting in. Any template visible to the caller (own workspace, public, or starter) can be favorited.

Returns 404 when the template does not exist or is not visible to the caller. Calling this endpoint on an already-favorited template is idempotent (returns 200 with the template). Use DELETE /templates/{id}/favorite to remove. Does not require X-Workspace-Id.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.favorite_template(
    template_id="template_id",
)

⚙️ Parameters

template_id: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.templates.unfavorite_template(...) -> ApiResponseDict

📝 Description

Remove a template from the current user's favorites.

Idempotent and non-enumerating: returns an empty success response whether or not the favorite or the template exists. Does not require X-Workspace-Id.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.templates.unfavorite_template(
    template_id="template_id",
)

⚙️ Parameters

template_id: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

voices

client.voices.list(...) -> ApiCountedListResponseVoiceOut

📝 Description

List TTS voices available to the current workspace.

Every filter accepts repeat-key OR semantics: ?gender=female&gender=neutral&category=narration&source=platform&source=workspace. Filters combine across fields with AND; within a field, values OR.

language matches a voice when any of its declared locales matches any requested value. Platform voices with no declared locales (catalog gaps) are treated as general-use and match every language filter. User-uploaded / cloned voices with no declared locales are excluded — that state means "language unknown" pending the clone flow's language detection.

Multi-sort: sort and order are parallel lists. ?sort=uses_count&sort=name&order=desc&order=asc orders primarily by uses_count DESC, secondarily by name ASC. When order is shorter than sort, missing entries default per-field: name=asc, created_at=desc, uses_count=desc. When sort is omitted, list defaults to newest-first (or most-recently-favorited-first if favorites_only=true). Every sort path appends Voice.id ASC as a deterministic tiebreaker for pagination stability.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.voices.list()

⚙️ Parameters

offset: typing.Optional[int] — Number of results to skip for pagination.

limit: typing.Optional[int] — Maximum number of results to return (1–100).

favorites_only: typing.Optional[bool] — When true, return only voices in the workspace's favorites list.

source: typing.Optional[typing.List[ListVoicesRequestSourceItem]] — Repeat for OR across scopes: platform for system-provided voices, workspace for workspace-owned voices.

gender: typing.Optional[typing.List[VoiceGender]] — Repeat for OR

age: typing.Optional[typing.List[VoiceAge]] — Repeat for OR

category: typing.Optional[typing.List[VoiceCategory]] — Repeat for OR

accent: typing.Optional[typing.List[VoiceAccent]] — Repeat for OR

search: typing.Optional[str] — Full-text search against voice name, description, and tags.

sort: typing.Optional[typing.List[ListVoicesRequestSortItem]] — Repeat for multi-sort. Pairs with order index-wise.

order: typing.Optional[typing.List[ListVoicesRequestOrderItem]] — Parallel to sort[]; shorter is padded with per-field defaults.

provider: typing.Optional[typing.List[ListVoicesRequestProviderItem]] — Repeat for OR, e.g. ?provider=elevenlabs&provider=rime

model: typing.Optional[typing.List[str]] — Repeat for OR. Filters platform voices by TTS model, e.g. ?model=arcana&model=sonic-2

language: typing.Optional[typing.List[ListVoicesRequestLanguageItem]] — Repeat for OR, e.g. ?language=en-us&language=ko-kr

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.voices.get_voice_facets(...) -> ApiResponseVoiceFacetsOut

📝 Description

Filter-bar options (chips) for the voice browser, one list per dimension.

Returns providers, models, languages (data-driven) plus genders, ages, categories, accents (fixed enums) as VoiceFacetItem[] so the FE builds the whole filter bar — with per-chip count badges — in a single request instead of hardcoding option lists (mirrors GET /dictionary/languages). Each item is {value, label, count}: value is passed straight back to GET /voices; label is the display name for providers/models and null elsewhere (the FE owns language + enum labels); count is the number of matching voices. For languages/models, count counts only voices that explicitly declare the value — "general-use" platform voices (no declared locales/models) that GET /voices matches against every language/model filter are not counted, so a chip's count can be lower than the GET /voices result.

Accepts the SAME filters as GET /voices (tab scope source/favorites_only, plus provider/model/language/gender/age/category/accent/search). count is context-aware (faceted search): each dimension's counts apply every OTHER active filter but exclude that dimension's own selection — e.g. with provider=elevenlabs the language counts are scoped to ElevenLabs, while the provider chips still show every provider so the caller can switch.

Count-0 policy: data-driven dimensions omit count-0 values (only present ones, each a valid GET /voices filter — providers/models restricted to the enabled catalog, languages to the supported-locale allowlist, so a chip never 422s). Enum dimensions always return the full enum in natural order, count-0 included, for the FE to grey out.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.voices.get_voice_facets()

⚙️ Parameters

favorites_only: typing.Optional[bool] — Favorites tab scope

source: typing.Optional[typing.List[GetVoiceFacetsApiV1VoicesFacetsGetRequestSourceItem]] — Tab scope — repeat for OR, same values as GET /voices (e.g. platform, workspace)

gender: typing.Optional[typing.List[VoiceGender]] — Repeat for OR

age: typing.Optional[typing.List[VoiceAge]] — Repeat for OR

category: typing.Optional[typing.List[VoiceCategory]] — Repeat for OR

accent: typing.Optional[typing.List[VoiceAccent]] — Repeat for OR

search: typing.Optional[str]

provider: typing.Optional[typing.List[str]] — Repeat for OR, e.g. ?provider=elevenlabs&provider=rime

model: typing.Optional[typing.List[str]] — Repeat for OR. Filters platform voices by TTS model, e.g. ?model=arcana&model=sonic-2

language: typing.Optional[typing.List[str]] — Repeat for OR, e.g. ?language=en-us&language=ko-kr

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.voices.get(...) -> ApiResponseVoiceOut

📝 Description

Fetch a single voice by its ID.

Returns both platform (system-wide) voices and voices that belong to the caller's workspace. Returns 404 when the voice does not exist or is not accessible to the caller's workspace. The sample_url field is a time-limited presigned URL valid for 1 hour; regenerate it by calling this endpoint again rather than caching it long-term.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.voices.get(
    voice_id="voice_id",
)

⚙️ Parameters

voice_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.voices.similar(...) -> ApiListResponseVoiceSimilarOut

📝 Description

Return voices acoustically similar to a reference voice.

Results are ranked by semantic similarity score (descending) and include the reference voice's workspace voices and all platform voices. Each result includes a similarity_score between 0 and 1. Optionally filter by one or more language BCP-47 codes (repeat the parameter for OR semantics); up to 16 language values are accepted. Returns 503 when the reference voice has no embedding yet — retry after the indicated Retry-After interval. Prefer this endpoint over GET /voices with manual filtering when building a "voices like this" recommendation UI.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.voices.similar(
    voice_id="voice_id",
)

⚙️ Parameters

voice_id: str

limit: typing.Optional[int] — Number of similar voices to return (1–50).

language: typing.Optional[typing.List[str]] — Repeat for OR, e.g. ?language=en-us&language=ko-kr

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.voices.favorite_voice(...) -> ApiResponseVoiceOut

📝 Description

Add a voice to the current workspace's favorites.

Favorites are workspace-scoped, not per-user: all members of the workspace see the same favorited set. Idempotent — favoriting a voice that is already favorited succeeds without error. Returns the voice with is_favorite=true. Requires the caller to have at least editor role in the workspace.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.voices.favorite_voice(
    voice_id="voice_id",
)

⚙️ Parameters

voice_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.voices.unfavorite_voice(...) -> ApiResponseDict

📝 Description

Remove a voice from the current workspace's favorites.

Idempotent — removing a voice that is not currently favorited succeeds without error. Requires the caller to have at least editor role in the workspace.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.voices.unfavorite_voice(
    voice_id="voice_id",
)

⚙️ Parameters

voice_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

workspace

client.workspace.get_workspace_settings(...) -> ApiResponseWorkspaceSettingsOut

📝 Description

Return workspace-level settings for the specified workspace.

Currently exposes default_language (the locale used as the default for new workflow nodes) and theme (the workspace's display color theme). Settings are workspace-scoped: all members of the workspace share the same values. Per-user preferences (e.g. personal dark mode) are outside the scope of this endpoint.

If settings have not yet been explicitly configured for the workspace, defaults are returned (and persisted) on first access.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspace.get_workspace_settings(
    ws_id="ws_id",
)

⚙️ Parameters

ws_id: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

workspace-members

client.workspace_members.list_members(...) -> ApiListResponseWorkspaceMemberOut

📝 Description

List active members and pending invites for a workspace.

Returns a unified list combining confirmed members (status active) and outstanding invites that have not yet been accepted or revoked (status invited). Pending invites appear with user_id: null and only the email and role fields populated.

The list is sorted: the requesting user appears first, then admins by join date, then other members by join date. No pagination — the full roster is returned in a single response.

Roles:

  • admin: can manage members, invites, workspace settings, and all content.
  • editor: can create, edit, and run workflows; cannot manage members.
  • viewer: read-only access to workspace content and run history.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspace_members.list_members(
    ws_id="ws_id",
)

⚙️ Parameters

ws_id: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspace_members.create_invite(...) -> ApiResponseWorkspaceInviteOut

📝 Description

Invite a user to the workspace by email. Admin only.

Creates a pending invite and sends an invitation email to the specified address. The invitee does not need to have an existing account — they can sign up after receiving the invite. The invite includes a role (admin, editor, or viewer) that the invitee will receive upon accepting.

Invites expire after 14 days. Only one pending invite per email address per workspace is allowed at a time; re-inviting the same address while a pending invite exists returns 409. Inviting an address that already belongs to an active member also returns 409.

The total number of active members plus pending invites is counted against the workspace owner's plan seat limit. Exceeding the limit returns 402. The invitee's role can be updated before acceptance via PATCH /workspaces/{ws_id}/invites/{invite_id}, or the invite can be cancelled via DELETE /workspaces/{ws_id}/invites/{invite_id}.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspace_members.create_invite(
    ws_id="ws_id",
    email="email",
    role="admin",
)

⚙️ Parameters

ws_id: str

email: str — Email address to invite. Normalized to lowercase. Returns 409 if this address is already an active member or has a pending invite.

role: WorkspaceRole — Role to grant when the invite is accepted: admin, editor, or viewer.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspace_members.remove_member(...) -> ApiResponseDict

📝 Description

Remove an active member from the workspace. Admin only.

The removed member immediately loses access to all workspace resources. They receive an email notification informing them they have been removed.

Two protections prevent accidental lockouts:

  • The workspace owner cannot be removed.
  • The last remaining admin cannot be removed (returns 409).

Removing a member does not affect their account or other workspaces. To block an invited but not-yet-accepted user instead, revoke the invite via DELETE /workspaces/{ws_id}/invites/{invite_id}.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspace_members.remove_member(
    ws_id="ws_id",
    member_id="member_id",
)

⚙️ Parameters

ws_id: str

member_id: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspace_members.update_member_role(...) -> ApiResponseDict

📝 Description

Change a workspace member's role. Admin only.

Updates the role of an active member to admin, editor, or viewer. The operation is idempotent — setting a member to their current role succeeds silently (no error, no duplicate email notification).

Two protections prevent accidental lockouts:

  • The workspace owner's role cannot be changed.
  • The last remaining admin cannot be demoted (returns 409).

When the role actually changes, the affected member receives an email notification describing their new permissions.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspace_members.update_member_role(
    ws_id="ws_id",
    member_id="member_id",
    role="admin",
)

⚙️ Parameters

ws_id: str

member_id: str

request: WorkspaceMemberRoleUpdate

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspace_members.revoke_invite(...) -> ApiResponseDict

📝 Description

Cancel a pending invite so the invitee can no longer accept it. Admin only.

The invite token is invalidated immediately. If the invitee attempts to accept after revocation, they receive a 410 Gone. The invite is removed from the pending list returned by GET /workspaces/{ws_id}/members.

Revoking an invite that is already accepted, revoked, or expired returns 404. To remove an already-accepted member, use DELETE /workspaces/{ws_id}/members/{member_id}.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspace_members.revoke_invite(
    ws_id="ws_id",
    invite_id="invite_id",
)

⚙️ Parameters

ws_id: str

invite_id: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspace_members.update_invite_role(...) -> ApiResponseWorkspaceInviteOut

📝 Description

Change the role on a pending (not yet accepted) invite. Admin only.

Updates the role the invitee will receive when they accept. Only pending invites can be updated — attempting to update an accepted, revoked, or expired invite returns 409. The invitee is not notified of the role change; the updated role takes effect when they accept.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspace_members.update_invite_role(
    ws_id="ws_id",
    invite_id="invite_id",
    role="admin",
)

⚙️ Parameters

ws_id: str

invite_id: str

request: WorkspaceMemberRoleUpdate

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspace_members.accept_invite(...) -> ApiResponseDict

📝 Description

Accept a workspace invite using the token from the invitation email.

The token path parameter comes from the invitation link sent to the invitee's email. The caller must be authenticated and their verified email address must match the address the invite was sent to (403 if it does not).

On success the caller is added to the workspace with the role specified in the invite, and workspace_id is returned so the caller can immediately begin using that workspace. If the caller is already a member of the workspace (e.g. accepted via a different device), the accept is idempotent and returns the same workspace_id.

Error cases (all return 410 Gone):

  • Invite already accepted.
  • Invite was revoked by an admin.
  • Invite has expired (14-day TTL from creation).

The workspace owner's plan seat limit is re-checked at accept time in case the plan was downgraded after the invite was sent; exceeding the limit returns 402.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspace_members.accept_invite(
    token="token",
)

⚙️ Parameters

token: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

workspaces

client.workspaces.list_workspaces(...) -> ApiListResponseWorkspaceOut

📝 Description

List all workspaces the current user is a member of.

Returns workspaces where the caller has any role (admin, editor, or viewer), including workspaces they own and workspaces they joined via invite. Results are paginated; omits soft-deleted workspaces and the internal system workspace.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspaces.list_workspaces()

⚙️ Parameters

offset: typing.Optional[int]

limit: typing.Optional[int]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspaces.create_workspace(...) -> ApiResponseWorkspaceOut

📝 Description

Create a new workspace owned by the current user.

Workspaces are the top-level container for all resources (workflows, voices, dictionary entries, members). Every resource is scoped to exactly one workspace via the X-Workspace-Id header on subsequent requests.

The authenticated user becomes the workspace owner and is automatically added as an admin member. An optional slug (1–50 characters, lowercase kebab-case) can be supplied for a human-readable workspace identifier; if omitted, one is auto-generated from name. Returns 409 if the slug is already taken, 422 if the slug format is invalid or uses a reserved word.

The number of workspaces a user may own is plan-gated. Attempting to exceed the limit returns 402.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspaces.create_workspace(
    name="name",
)

⚙️ Parameters

name: str — Human-readable workspace name (1–200 characters, non-blank).

slug: typing.Optional[str] — Optional URL-safe identifier (lowercase kebab-case, 1–50 characters). Auto-generated from name if omitted. Returns 409 if taken, 422 if invalid or reserved.

color_idx: typing.Optional[int] — Index into the workspace color palette (0–6).

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspaces.slug_available(...) -> ApiResponseSlugAvailabilityOut

📝 Description

Check whether a slug is available for the current workspace. Admin only.

Returns { available: true } if the slug is valid, not reserved, and not already claimed by another workspace. When unavailable, reason indicates why: invalid (format/length), reserved (blocked word), or taken (already in use globally). The workspace's own current slug is self-excluded, so an admin can safely check their existing slug without receiving taken.

This is an advisory point-in-time check — a concurrent POST /workspaces or PATCH /workspaces/{id} from another session can claim the slug between this response and the caller's write. Always handle 409 WORKSPACE_SLUG_TAKEN on create_workspace and update_workspace.

Requires the X-Workspace-Id header (the workspace being renamed) and admin role in that workspace. Missing/invalid header returns 400; not a member returns 404; not admin returns 403.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspaces.slug_available(
    slug="slug",
)

⚙️ Parameters

slug: str — Candidate slug; normalized (strip().lower()) before checks.

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspaces.get_workspace(...) -> ApiResponseWorkspaceOut

📝 Description

Fetch a single workspace by ID.

Returns the workspace if the current user is an active member (any role). Returns 404 if the workspace does not exist, has been deleted, or the caller is not a member — the two cases are intentionally indistinguishable to prevent workspace enumeration.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspaces.get_workspace(
    workspace_id="workspace_id",
)

⚙️ Parameters

workspace_id: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspaces.delete_workspace(...) -> ApiResponseDict

📝 Description

Delete a workspace and all of its resources. Owner only.

Soft-deletes the workspace and cascades to all owned resources (workflows, voices, dictionary entries, members, etc.). The workspace and its contents become inaccessible via the API immediately. Data is retained for the GDPR retention period before permanent purge.

Only the workspace owner (the user who created it) can delete it; admin members who are not the owner receive 404. Returns 404 if the workspace does not exist or the caller is not the owner.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspaces.delete_workspace(
    workspace_id="workspace_id",
)

⚙️ Parameters

workspace_id: str

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workspaces.update_workspace(...) -> ApiResponseWorkspaceOut

📝 Description

Update a workspace's name, color palette index, and/or slug. Admin only.

All fields are optional — supply only the fields you want to change. slug follows the same validation rules as on create (lowercase kebab-case, 1–50 characters, no reserved words). Returns 409 if the new slug is already claimed by another workspace, 422 if the slug format is invalid or reserved. Re-setting the workspace's current slug to itself never returns 409.

Only workspace admins may call this endpoint; other members receive 404 (same as not-found, to avoid leaking membership details to non-members).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workspaces.update_workspace(
    workspace_id="workspace_id",
)

⚙️ Parameters

workspace_id: str

name: typing.Optional[str] — New workspace name. Omit to leave unchanged.

slug: typing.Optional[str] — New slug (lowercase kebab-case, 1–50 characters). Omit to leave unchanged. Returns 409 if taken, 422 if invalid or reserved.

color_idx: typing.Optional[int] — New color palette index (0–6). Omit to leave unchanged.

routing_price_sensitivity: typing.Optional[float] — New voice-selection price/quality balance (0.0 = pure quality, 1.0 = pure price, 0.5 = balanced). Omit to leave unchanged.

routing_llm_fit: typing.Optional[bool] — New setting for whether automatic voice selection also weighs content fit. Omit to leave unchanged.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

uploads

client.uploads.create(...) -> ApiResponseUploadCreateResponse

📝 Description

Request a presigned URL to upload a file to object storage (step 1 of 2).

The two-step upload flow:

  1. POST /uploads — register the file and receive a short-lived upload_url. PUT your file bytes directly to that URL (do not send them to this API).
  2. POST /uploads/{id} — confirm the upload completed and bind the file to a resource (e.g. a workflow). The file is moved to its final location and the upload record transitions from pending to uploaded.

category controls which file formats are accepted:

  • script — text-based formats (txt, srt, csv, json, xliff, docx)
  • dictionary — audio formats (mp3, wav, m4a, ogg, webm)

The presigned URL expires within a short window (see upload_url TTL in the response). If the URL expires before the PUT completes, discard this upload record and start over with a fresh POST /uploads call.

X-Workspace-Id is optional but recommended for workspace-scoped storage quota tracking. API keys with a bound workspace attach automatically.

Dual-auth: Bearer JWT or API key (scope uploads:write).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.uploads.create(
    filename="filename",
    category="script",
)

⚙️ Parameters

filename: str — Original filename including extension (e.g. script.txt). Must include a file extension.

category: UploadRequestCategory — File category. Determines which formats are accepted: script for text formats (txt, srt, csv, json, xliff, docx); dictionary for audio formats (mp3, wav, m4a, ogg, webm).

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.uploads.confirm(...) -> ApiResponseUploadOut

📝 Description

Confirm a completed upload and bind it to a resource (step 2 of 2).

Call this after successfully PUTting your file to the presigned URL returned by POST /uploads. Provide context_type and context_id to associate the file with an existing resource (currently workflow is the supported context type). The file is moved to its final location and status transitions from pending to uploaded.

This endpoint is idempotent: if the upload was already confirmed, the current state is returned without re-processing.

Storage quota is checked against the workspace at confirm time. If confirming would exceed the workspace storage limit, a 402 is returned and the file remains in its staging location (the upload record stays pending so you can delete the staging file and try a smaller file).

Binding to a workspace-scoped resource requires the caller to be a member of that workspace. Workspace is inferred from the resource when X-Workspace-Id is omitted.

Dual-auth: Bearer JWT or API key (scope uploads:write).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.uploads.confirm(
    upload_id="upload_id",
    context_type="workflow",
    context_id="context_id",
)

⚙️ Parameters

upload_id: str

context_type: UploadConfirmRequestContextType — Type of resource this upload is being attached to. Currently only workflow is supported.

context_id: str — ID of the resource to attach this upload to. Must be an existing resource of the given context_type that the caller has access to.

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.uploads.delete(...) -> ApiResponseDict

📝 Description

Delete an upload and its associated file.

Permanently removes the upload record and schedules the stored file for deletion. The record is removed first; the file is cleaned up asynchronously after the response so storage removal only happens after a successful commit.

If the upload was previously confirmed against a workspace-scoped resource, the consumed storage bytes are released back to the workspace quota, keeping the workspace storage counter accurate.

Callers can delete uploads in any state (pending or uploaded). Deleting a pending upload (e.g. after an expired presigned URL) is the correct way to clean up an abandoned upload attempt.

Dual-auth: Bearer JWT or API key (scope uploads:write).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.uploads.delete(
    upload_id="upload_id",
)

⚙️ Parameters

upload_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

usage

client.usage.usage_summary(...) -> ApiResponseUsageSummaryOut

📝 Description

Return aggregated usage totals and activity chart data for the workspace.

Combines credit consumption, character and line counts, and workflow run statistics for the requested rolling window (range) with a chart-ready activity series (activity) bucketed by activity_view.

The credits.used field reflects the authenticated user's own billing-period consumption; all other aggregate fields (characters, lines, runs, daily buckets, activity buckets) are workspace-scoped across all members.

Date boundaries are computed in the supplied timezone (IANA, e.g. America/New_York) so "today" and "this week" align with the caller's local calendar. Defaults to UTC.

Use GET /usage/by-language for a language-level breakdown, or GET /usage/activity for the event-by-event feed.

Dual-auth: Bearer JWT or API key (scope workspace:read).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.usage.usage_summary()

⚙️ Parameters

range: typing.Optional[UsageSummaryApiV1UsageSummaryGetRequestRange] — Rolling local calendar-day range.

activity_view: typing.Optional[UsageSummaryApiV1UsageSummaryGetRequestActivityView] — Activity chart view: daily=7 local days, weekly=12 Monday-start weeks, monthly=12 months.

timezone: typing.Optional[str] — IANA timezone for local day bucketing.

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.usage.usage_by_language(...) -> ApiResponseUsageByLanguageOut

📝 Description

Return workspace audio generation usage broken down by language.

Each row represents one locale with its share of total credit and character consumption. share is a 0..1 fraction of workspace-wide usage for the period; multiply by 100 for a percentage.

Period selection: supply activity_view to align the language rows with the same period shown on the Usage dashboard chart (daily = last 7 local days, weekly = last 12 Monday-start weeks, monthly = last 12 months). When activity_view is provided, range is ignored and range in the response is null. Omit activity_view to use the rolling range window instead.

Date boundaries are computed in the supplied timezone (IANA). Defaults to UTC.

Dual-auth: Bearer JWT or API key (scope workspace:read).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.usage.usage_by_language()

⚙️ Parameters

range: typing.Optional[UsageByLanguageApiV1UsageByLanguageGetRequestRange] — Rolling local calendar-day range.

activity_view: typing.Optional[UsageByLanguageApiV1UsageByLanguageGetRequestActivityView] — Optional activity view period to align language rows with the selected Usage tab. When supplied, range is ignored and range in the response is null.

timezone: typing.Optional[str] — IANA timezone for local day bucketing.

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.usage.usage_activity(...) -> ApiListResponseUsageActivityOut

📝 Description

Return the workspace activity feed as a cursor-paginated event list.

Each item represents a discrete workspace event (workflow run, voice generated, template applied, member invited, API key created, settings changed). Events are ordered newest-first within the requested rolling window.

Filtering:

  • type narrows to a single action kind (e.g. workflow_run).
  • user_id restricts to events triggered by a specific workspace member; returns 404 if the user is not a member of this workspace.
  • Both filters can be combined.

Pagination: pass the cursor value from a previous response to retrieve the next page. An absent or null cursor in the response means no further pages exist. Page size is controlled by limit (1–100, default 20).

Date boundaries are computed in the supplied timezone (IANA). Defaults to UTC.

Dual-auth: Bearer JWT or API key (scope workspace:read).

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.usage.usage_activity()

⚙️ Parameters

range: typing.Optional[UsageActivityApiV1UsageActivityGetRequestRange] — Rolling local calendar-day range.

type: typing.Optional[UsageActivityAction] — Filter by usage activity type.

user_id: typing.Optional[str] — Filter by actor user id.

limit: typing.Optional[int]

cursor: typing.Optional[str] — Opaque pagination cursor.

timezone: typing.Optional[str] — IANA timezone for local day bucketing.

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

users

client.users.get_my_credits() -> ApiResponseBalanceResponse

📝 Description

Return the caller's current credit balance and billing period details.

balance is the authoritative gate value: use it to decide whether to attempt a workflow run. remaining is a display convenience derived from settled ledger entries and may temporarily exceed balance while a workflow run holds an open reserve. used reflects credits consumed in the current billing period. plan_grant is the total monthly credit allowance for the caller's plan, enabling a "X / Y used" display. period_start and period_end mark the boundaries of the current billing window; free-tier callers use a calendar-month boundary.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.users.get_my_credits()

⚙️ Parameters

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.users.get_my_plan_limits() -> ApiResponsePlanLimits

📝 Description

Return the plan limits that govern the caller's current tier.

Includes numeric quotas (monthly_credits, concurrent_runs_per_user, storage_bytes_per_workspace, workspaces_per_owner) and feature flags (byok_enabled, auto_fix_enabled, auto_edit_enabled). null on list fields such as tts_models_allowlist or supported_languages means all available options are permitted. Use this endpoint to gate feature access in your application rather than hardcoding tier names, which may change.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.users.get_my_plan_limits()

⚙️ Parameters

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.users.get_current_notification_preferences() -> ApiResponseEmailNotificationPreferencesOut

📝 Description

Return the caller's current email notification settings.

Each boolean field corresponds to a notification category. true means the caller will receive that email; false means they have opted out. Use PATCH /me/notification-preferences to change individual preferences.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.users.get_current_notification_preferences()

⚙️ Parameters

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.users.update_current_notification_preferences(...) -> ApiResponseEmailNotificationPreferencesOut

📝 Description

Partially update the caller's email notification preferences.

Send only the fields you want to change; omitted fields are left unchanged. All provided fields must be boolean — explicit null values are rejected with a 422. Returns the full updated preference object.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.users.update_current_notification_preferences()

⚙️ Parameters

completed_generation_email: typing.Optional[bool] — Set to true to enable or false to disable completion emails. Omit to leave unchanged.

failed_generation_email: typing.Optional[bool] — Set to true to enable or false to disable failure emails. Omit to leave unchanged.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.users.list_my_templates(...) -> ApiListResponseTemplateOut

📝 Description

List workflow templates created by the caller in the current workspace.

Returns only templates owned by the caller; templates shared by other workspace members or platform starter templates are not included — use GET /api/v1/templates for the full gallery. Supports offset-based pagination via offset / limit. Combine category, search, and favorites_only to narrow results; multiple category values are OR'd.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.users.list_my_templates()

⚙️ Parameters

category: typing.Optional[typing.List[TemplateCategory]] — Repeat for OR, e.g. ?category=media&category=creative

search: typing.Optional[str] — Full-text search against template name and description.

sort: typing.Optional[ListMyTemplatesApiV1UsersMeTemplatesGetRequestSort] — Sort order: recent (last updated), name (A–Z), or uses (most used).

offset: typing.Optional[int] — Number of results to skip for pagination.

limit: typing.Optional[int] — Maximum number of results to return (1–100).

favorites_only: typing.Optional[bool]

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

workflows

client.workflows.list(...) -> ApiCountedListResponseWorkflowListItem

📝 Description

List workflows in the current workspace.

Returns a counted, paginated list of workflows scoped to the X-Workspace-Id header. Each item includes aggregate stats (runs_count, last_run_at, last_run_status) computed over all runs for that workflow.

Status filter: status narrows by the UI-derived state of the workflow's most recent run. completed matches only workflows whose latest run succeeded (completed-only), and failed matches only workflows whose latest run failed — the two buckets are disjoint. A workflow whose latest run was cancelled matches neither bucket and surfaces only in the unfiltered list. running matches active (running or paused) workflows. draft matches workflows with no runs yet. paused is accepted but currently returns no results.

Multi-sort: sort and order are parallel query lists. ?sort=runs_count&sort=name&order=desc&order=asc orders primarily by runs_count DESC, then by name ASC. When order has fewer entries than sort, missing positions use per-field defaults (name=asc, updated_at=desc, runs_count=desc). Omitting sort defaults to updated_at DESC. A stable id ASC tiebreaker is always appended so offset/limit pagination is consistent when sort keys tie.

Date range: last_run_after / last_run_before filter by the time of the most recent run. Both must be ISO 8601 with a UTC offset; a naive datetime returns 422. An inverted range (after > before) also returns 422.

Failure history: has_failed_run is orthogonal to status. status keys off the latest run only; has_failed_run=true matches workflows with a failed run anywhere in their history, so a workflow whose latest run completed still matches if an earlier run failed. It composes with the other filters (AND). cancelled runs do not count as failures.

pagination.total reflects the filtered count for the current query.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.list()

⚙️ Parameters

status: typing.Optional[WorkflowListStatus] — UI workflow status filter. completed matches workflows whose latest run succeeded (completed-only); failed matches failed-only — the two are disjoint. A workflow whose latest run was cancelled matches neither and appears only in the unfiltered list. paused is accepted for forward compatibility and currently returns no rows.

search: typing.Optional[str] — Case-insensitive search over name and description.

sort: typing.Optional[typing.List[ListWorkflowsRequestSortItem]] — Repeat for multi-sort. Pairs with order index-wise.

order: typing.Optional[typing.List[ListWorkflowsRequestOrderItem]] — Parallel to sort[]; shorter is padded with per-field defaults.

last_run_after: typing.Optional[datetime.datetime] — Filter workflows whose last_run_at is at or after this ISO datetime (ISO 8601 with UTC offset required).

last_run_before: typing.Optional[datetime.datetime] — Filter workflows whose last_run_at is at or before this ISO datetime (ISO 8601 with UTC offset required).

has_failed_run: typing.Optional[bool] — Filter by failure history — ORTHOGONAL to status (which is latest-run based). true returns only workflows with at least one run that ended in failed state anywhere in their history; a workflow whose latest run succeeded still matches if an earlier run failed. false returns only workflows that have never had a failed run. Composes (ANDs) with status/search/date filters. cancelled runs are not treated as failures.

offset: typing.Optional[int] — Zero-based pagination offset.

limit: typing.Optional[int] — Maximum items to return (1–100).

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.create_workflow(...) -> ApiResponseWorkflowOut

📝 Description

Create a new workflow in the current workspace.

Validates the workflow definition (graph structure, node types, edge connectivity) before persisting. Returns 422 with structured details if the definition fails validation. Requires at least editor role in the workspace; viewers cannot create workflows.

The definition contains a graph (nodes and edges) and an execution block (ordered step list and execution params). Omitting definition creates a workflow with an empty graph that can be edited later.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.create_workflow(
    name="name",
)

⚙️ Parameters

name: str — Human-readable workflow name (1–200 characters, non-blank).

workspace_id: typing.Optional[str]

description: typing.Optional[str] — Optional description shown in the workflow list (max 5000 characters).

definition: typing.Optional[WorkflowDefinitionInput] — Graph and execution config. Omit to create an empty workflow.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.get(...) -> ApiResponseWorkflowOut

📝 Description

Fetch a single workflow by ID.

Returns the full workflow including its definition (graph nodes/edges and execution config), aggregate run stats, and the latest run status. The definition is returned with any backwards-compatible config migrations applied, so node configs always reflect the current schema even if the workflow was saved with an older version.

Use GET /workflows to list multiple workflows without fetching their full definitions.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.get(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.update_workflow(...) -> ApiResponseWorkflowOut

📝 Description

Replace a workflow's name, description, and definition (full update).

All fields in the request body are required. The definition is validated before persisting; an invalid graph returns 422. Existing runs are not affected — each run captures a definition_snapshot at start time. Requires at least editor role. Use PATCH to update only specific fields without supplying the full definition.

🔌 Usage

from onepin import OnePinClient, WorkflowDefinitionInput
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.update_workflow(
    workflow_id="workflow_id",
    name="name",
    definition=WorkflowDefinitionInput(),
)

⚙️ Parameters

workflow_id: str

name: str — Human-readable workflow name (1–200 characters, non-blank).

definition: WorkflowDefinitionInput — Full replacement graph and execution config. Must be valid.

workspace_id: typing.Optional[str]

description: typing.Optional[str] — Optional description (max 5000 characters). Pass null to clear.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.delete_workflow(...) -> ApiResponseDict

📝 Description

Delete a workflow and hide it from all list and get endpoints.

The workflow is soft-deleted: its data (including runs and their outputs) is retained for audit and GDPR-purge purposes but is no longer accessible via the API. Subsequent GET, PUT, PATCH, or run requests on the same ID return 404. Requires at least editor role.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.delete_workflow(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.patch_workflow(...) -> ApiResponseWorkflowOut

📝 Description

Partially update a workflow — only supplied fields are changed.

Any combination of name, description, and definition may be included; omitted fields are left unchanged. At least one field must be present (empty body returns 422). If definition is provided it is fully validated; an invalid graph returns 422. Requires at least editor role.

Use PUT when replacing the full workflow definition in one operation.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.patch_workflow(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

workspace_id: typing.Optional[str]

name: typing.Optional[str] — New workflow name (1–200 characters). Omit to leave unchanged.

description: typing.Optional[str] — New description (max 5000 characters). Omit to leave unchanged; pass null to clear.

definition: typing.Optional[WorkflowDefinitionInput] — Replacement graph and execution config. Omit to leave unchanged; must be valid if supplied.

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.list_workflow_uploads(...) -> ApiListResponseUploadOut

📝 Description

List confirmed uploads attached to a workflow.

Returns only uploads that have been confirmed (fully transferred and committed to the workflow). In-progress or abandoned uploads are excluded. Each item includes a short-lived download URL for the uploaded file.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.list_workflow_uploads(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

offset: typing.Optional[int] — Zero-based pagination offset.

limit: typing.Optional[int] — Maximum items to return (1–100).

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.estimate_workflow(...) -> ApiResponseEstimateResponse

📝 Description

Estimate the credit cost of running a workflow without creating a run.

Computes a breakdown of expected credits per node type based on the workflow's current definition. No run is created, no credits are charged, and no side effects occur. Equivalent to POST /runs/preview; prefer that path in new integrations as it is co-located with the run lifecycle.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.estimate_workflow(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.preview_run(...) -> ApiResponseEstimateResponse

📝 Description

Dry-run credit estimate for a workflow — no run is created.

Returns a per-node-type credit breakdown based on the workflow's current definition. No run is enqueued, no credits are charged, and the workflow state is not modified. Use this before calling POST /runs to confirm the expected cost. Equivalent to POST /estimate.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.preview_run(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.runs_summary(...) -> ApiResponseRunsSummaryOut

📝 Description

Aggregate run statistics for a workflow over an optional date window.

Returns per-status counts (completed, failed, cancelled, pending, running, paused) plus two derived metrics:

  • pass_rate: completed / (completed + failed + cancelled). null when there are no terminal runs in the window.
  • average_duration_seconds: mean of completed_at - started_at over successfully completed runs only. null when no runs have completed.

Date range: from / to filter by created_at. Both must be ISO 8601 with a UTC offset; a naive datetime returns 422. An inverted range (from > to) also returns 422. Omit both to aggregate over all runs.

Use GET /runs with ?status= filters for individual run details; this endpoint is best for dashboard-style health metrics.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.runs_summary(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

from: typing.Optional[datetime.datetime] — Filter runs by created_at >= this ISO datetime (ISO 8601 with UTC offset required).

to: typing.Optional[datetime.datetime] — Filter runs by created_at <= this ISO datetime (ISO 8601 with UTC offset required).

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.get_run_steps(...) -> ApiResponseListWorkflowRunStepOut

📝 Description

List per-node execution steps for a workflow run.

Returns one entry per node execution attempt, ordered by execution sequence. Each step includes the node type, status, iteration number (for nodes that are retried), start/completion timestamps, and the node's result output.

For audio output nodes, result is hydrated with short-lived playback_url values (valid for 15 minutes) so callers can stream audio directly without a separate download step.

node_display_name is resolved from the run's definition snapshot, so it reflects the name the node had when the run executed. Nodes that were retried appear as multiple steps with incrementing iteration values.

For a higher-level view with aggregated metrics (pass rates, audio duration by language), use GET /runs/{run_id}/overview. For paginated, grouped script+audio rows suitable for a data table, use GET /runs/{run_id}/data.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.get_run_steps(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.get_run_overview(...) -> ApiResponseWorkflowRunOverviewOut

📝 Description

Fetch server-computed overview aggregates for a workflow run.

Returns structured metric sections (e.g. audio duration totals, validation pass rates) grouped by display section, along with per-language audio breakdowns and per-validator scoring summaries. Also includes a workflow_snapshot with the graph definition and per-node completion states.

This endpoint is best suited for a summary/results view after a run completes. It differs from the other run sub-resources as follows:

  • GET /runs/{run_id} — full run record including the raw definition snapshot.
  • GET /runs/{run_id}/status — volatile status fields only; for polling.
  • GET /runs/{run_id}/steps — flat per-node step log with audio playback URLs.
  • GET /runs/{run_id}/data — paginated script+audio rows for a data table.
  • GET /runs/{run_id}/overview (this endpoint) — pre-aggregated metrics and node state map for a dashboard/overview panel.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.get_run_overview(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.get_run_data(...) -> WorkflowRunDataResponse

📝 Description

Paginated script-and-audio data rows for a completed workflow run.

Returns grouped rows where each row represents one source script line. Within each row, cards contain the per-language audio outputs, per-card validation scores (word accuracy, naturalness), and short-lived audio playback_url values (valid for 15 minutes).

Filtering:

  • search narrows which rows are returned based on their source script text.
  • language narrows the cards list within each returned row to a single locale. Rows with no matching cards are still returned (with empty cards), and pagination.total always reflects the search-filtered row count regardless of language.

Pagination: pagination.total is scoped to the search filter only.

Response includes a partial field indicating whether any data is still being computed (e.g. audio not yet generated, validation not yet scored). This endpoint sets Cache-Control: no-store because playback URLs are short-lived and data may change while a run is still in progress.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.get_run_data(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

search: typing.Optional[str] — Case-insensitive search over the source/script text of each row.

language: typing.Optional[str] — Exact full-locale code to filter cards within each row (e.g. en-US). _ is normalized to -. Filtering is card-level only — rows remain visible even when all their cards are filtered out, and pagination.total is unaffected.

offset: typing.Optional[int] — Zero-based pagination offset.

limit: typing.Optional[int] — Maximum rows to return (1–100).

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.download_run(...) -> ApiResponseDownloadUrlOut

📝 Description

Create a temporary download URL for a complete workflow run export.

Returns a pre-signed URL pointing to a ZIP archive containing all audio output files produced by the run. The URL is valid for 15 minutes (expires_at). The archive is generated on first request and cached for subsequent calls; re-calling this endpoint before expiry returns a new URL for the same cached archive.

Only available for runs in completed status — returns 409 for runs that are still active or ended in failed/cancelled. Returns 404 if the run produced no audio files.

To download output from a single output node rather than the whole run, use GET /runs/{run_id}/nodes/{node_id}/download.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.download_run(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.download_run_node(...) -> ApiResponseDownloadUrlOut

📝 Description

Create a temporary download URL for a single output node's audio export.

Returns a pre-signed URL for a ZIP archive containing the audio files produced by one specific output node within the run. Useful when a workflow has multiple output nodes and the caller wants only one node's results rather than the full run archive.

node_id must identify an output-category node in the run's definition snapshot. Passing a node ID that belongs to a non-output node type (e.g. a processing or validation node) returns 404. Returns 404 if the node produced no audio files, and 409 if the run has not yet completed.

The URL is valid for 15 minutes. To download all output nodes in a single archive, use GET /runs/{run_id}/download instead.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.download_run_node(
    workflow_id="workflow_id",
    run_id="run_id",
    node_id="node_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

node_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.pause_run(...) -> ApiResponseWorkflowRunOut

📝 Description

Pause an active workflow run at the next safe checkpoint.

For a running run, the current wave of parallel nodes is allowed to finish before the run parks (in-flight work is preserved, not abandoned). For a pending run that has not yet started, it parks immediately. The run transitions to paused status once drained; during the drain period, status remains running with pause_requested_at set.

The operation is idempotent: pausing an already-paused run returns it unchanged. A paused run can be resumed via POST /runs/{run_id}/resume or permanently stopped via POST /runs/{run_id}/cancel.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.pause_run(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.resume_run(...) -> ApiResponseWorkflowRunOut

📝 Description

Resume a paused workflow run from its last completed wave.

Transitions the run from paused back to running and schedules execution to continue from where it left off — no nodes that already completed are re-executed.

Returns 409 if the workspace already has another active run for this workflow, or if the caller is at the concurrent-run limit. In that case the run stays paused and the caller can retry later. Only runs in paused status can be resumed; attempting to resume a running, completed, failed, or cancelled run returns 409.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.resume_run(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.duplicate_workflow(...) -> ApiResponseWorkflowOut

📝 Description

Create a copy of an existing workflow in the same workspace.

The new workflow inherits the source's name (suffixed with " (Copy)"), description, and definition. Runs from the original workflow are not copied — the duplicate starts with zero runs. Requires at least editor role. Returns 201 with the new workflow on success.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.duplicate_workflow(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

Workflows Runs

client.workflows.runs.list(...) -> ApiCountedListResponseWorkflowRunListItem

📝 Description

List runs for a workflow, newest first by default.

Returns a counted, paginated list of runs for the specified workflow. Each item includes the run's status, step progress (total_steps, finished_steps), credit usage, and the actor who triggered it.

Status filter: Pass ?status=completed,failed to OR-match multiple statuses. Values must be the raw lowercase RunStatus strings. Unknown values or empty tokens (e.g. a,,b) return 422.

Pagination: pagination.total reflects the filtered count. Sort tiebreaks always append id ASC for stable offset/limit pagination.

For aggregate statistics (pass rate, average duration) across all runs, use GET /runs/summary instead.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.runs.list(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

offset: typing.Optional[int] — Zero-based pagination offset.

limit: typing.Optional[int] — Maximum items to return (1–100).

status: typing.Optional[str] — Comma-separated raw RunStatus values (e.g. completed,failed). Values are case-sensitive lowercase: pending, running, completed, failed, cancelled, paused. Multiple values OR-match. Empty tokens (e.g. a,,b) and unknown values return 422.

search: typing.Optional[str] — Case-insensitive search over the triggering user's display name (falls back to email).

sort: typing.Optional[ListRunsRequestSort] — Sort field: created_at | started_at | completed_at | status. Defaults to created_at.

order: typing.Optional[ListRunsRequestOrder]asc or desc. Defaults to desc.

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.runs.start(...) -> ApiResponseWorkflowRunOut

📝 Description

Start a new execution of a workflow (202 Accepted).

Enqueues the workflow for asynchronous execution and returns the newly created run in pending or running status. The run progresses through its nodes in the background; poll GET /runs/{run_id}/status for lightweight progress updates, or GET /runs/{run_id} once to load the immutable definition snapshot.

Use POST /runs/preview or POST /estimate to compute the credit cost before committing to an actual run — those endpoints are read-only and incur no charges.

Returns 409 if the workspace is at its concurrent-run limit or another run for this workflow is already active.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.runs.start(
    workflow_id="workflow_id",
)

⚙️ Parameters

workflow_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.runs.get(...) -> ApiResponseWorkflowRunDetailOut

📝 Description

Fetch full detail for a single workflow run.

Returns all run fields plus definition_snapshot — the graph and execution config captured at the moment the run started. The snapshot is returned raw (no config migrations applied), so it faithfully represents the workflow as it existed for this specific execution even if the workflow definition has since been edited.

This is the heaviest run endpoint. For progress polling, use the lighter GET /runs/{run_id}/status which omits the snapshot. For aggregated visual metrics, use GET /runs/{run_id}/overview. For the per-node step log with audio playback URLs, use GET /runs/{run_id}/steps.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.runs.get(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.runs.status(...) -> ApiResponseWorkflowRunStatusOut

📝 Description

Lightweight run status for progress polling.

Returns only the volatile, frequently-changing fields: status, step counts (total_steps, finished_steps), timestamps (started_at, completed_at, paused_at, pause_requested_at), usage_summary, error, and has_export. The definition snapshot is intentionally omitted to keep response size small.

Recommended polling pattern: call GET /runs/{run_id} once after starting a run to load the immutable definition snapshot and initial metadata, then poll this endpoint until status reaches a terminal value (completed, failed, or cancelled). has_export becoming true signals that a download is ready via GET /runs/{run_id}/download.

The transient pausing state is observable here: status == "running" with pause_requested_at set means a pause is in progress but the current wave has not yet finished draining.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.runs.status(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.

client.workflows.runs.cancel(...) -> ApiResponseWorkflowRunOut

📝 Description

Cancel an active workflow run.

Immediately marks the run as cancelled and stops any further processing. In-flight work at the current node may be abandoned mid-execution. The operation is idempotent: cancelling an already-cancelled run returns the run unchanged without error.

Only runs in pending, running, or paused status can be cancelled. Runs that have already reached a terminal state (completed, failed, cancelled) return 409.

Unlike pause, cancel is permanent — a cancelled run cannot be resumed. Use pause if you intend to continue the run later.

🔌 Usage

from onepin import OnePinClient
from onepin.environment import OnePinClientEnvironment

client = OnePinClient(
    token="<token>",
    environment=OnePinClientEnvironment.PROD,
)

client.workflows.runs.cancel(
    workflow_id="workflow_id",
    run_id="run_id",
)

⚙️ Parameters

workflow_id: str

run_id: str

workspace_id: typing.Optional[str]

request_options: typing.Optional[RequestOptions] — Request-specific configuration.