Skip to content

Index

API endpoint handlers for the YouSleep client.

AdminEndpoint

Bases: BaseEndpoint

Handle admin-specific API endpoints.

Provide methods for accounts, organizations (their limits and credits), and platform statistics. All methods require admin role.

adjust_organization_credits(org_id, adjustment) async

Add to or deduct from an organization's purchased credits.

A deduction never takes credits held for running analyses, so less than requested may be deducted.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
adjustment CreditAdjustment

The signed amount and the reason, kept on the ledger.

required

Returns:

Type Description
AdminOrganizationDetailOut

The organization with its new balance

Raises:

Type Description
NotFoundError

If the organization does not exist

ForbiddenError

If not an admin user

Example
org = await client.admin.adjust_organization_credits(
    org_id, CreditAdjustment(amount=50, reason="Pilot study")
)
print(org.credits.purchased.available)

approve_capability_request(request_id) async

Approve a request for a capability, which turns the capability on.

Refused unless the organization's owner has accepted the latest published site agreement, or the platform operates the organization.

Parameters:

Name Type Description Default
request_id UUID | str

The request id.

required

approve_credit_request(request_id, approval=None) async

Approve a grant or extension request, as asked or with changes.

Approving a grant request makes the grant; approving an extension moves the grant's expiry. The requester is emailed the decision.

Parameters:

Name Type Description Default
request_id UUID | str

The request id.

required
approval AdminCreditRequestApprove | None

A different amount or term, and a note for administrators; the request's own when omitted.

None

approve_runner(runner_id, fingerprint) async

Approve a pending runner, with the fingerprint read on its host.

The runner prints its key's fingerprint when it enrols. Compare it with the one on the host, not one passed along by someone else: the platform refuses an approval whose fingerprint differs from the enrolled key's.

Parameters:

Name Type Description Default
runner_id UUID | str

The runner to approve.

required
fingerprint str

The whole fingerprint, SHA256:….

required

Returns:

Type Description
RunnerOut

The runner, now active.

Raises:

Type Description
YouSleepClientError

If the fingerprint differs or the runner is not pending (409)

NotFoundError

If the runner does not exist

ForbiddenError

If not an admin user

approve_user(user_id) async

Approve a pending request-access account so its owner can log in.

Idempotent — approving an already-approved account is a no-op. To decline a request instead, use :meth:reject_user.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the pending user to approve

required

Returns:

Type Description
AdminUserOut

The approved user (approved_at now set)

Raises:

Type Description
NotFoundError

If user does not exist

ForbiddenError

If not an admin user

Example
for u in (await client.admin.list_users(pending=True)).users:
    await client.admin.approve_user(u.id)

cancel_analyses(request) async

Cancel analyses across accounts, one bounded page per call.

Address them by explicit ids, by a filter (an account, a set of live states, or both) with the count seen on the listing, or by all with that count. A live-set selector whose count moved since it was previewed is refused with a conflict. Each call handles at most the server's per-request cap; loop on summary.remaining.

Parameters:

Name Type Description Default
request AdminCancelAnalysesRequest

The selector and, for a live set, the expected count

required

Returns:

Type Description
AdminCancelAnalysesResponse

Per-item results and the page summary, including what remains

Raises:

Type Description
ConflictError

If a live set's count moved since it was previewed

ForbiddenError

If not an admin user

Example
page = await client.admin.list_analyses(user_id=user_id,
    status_any=[AnalysisStatus.PENDING])
request = AdminCancelAnalysesRequest(
    selector={"filter": {"user_id": user_id,
                         "status_any": [AnalysisStatus.PENDING]}},
    expected_count=page.total,
)
result = await client.admin.cancel_analyses(request)
while result.summary.remaining and result.summary.cancelled:
    request.expected_count = result.summary.remaining
    result = await client.admin.cancel_analyses(request)

clear_control(name) async

Clear a control's override; the configured value applies again at once.

Parameters:

Name Type Description Default
name PlatformControlName | str

The control

required

Returns:

Type Description
PlatformControlOut

The control as it now reads

Raises:

Type Description
ForbiddenError

If not an admin user

create_demo_dataset(dataset) async

Register a new demo dataset (or replace an existing one by name).

Parameters:

Name Type Description Default
dataset AdminDemoDatasetCreate

Demo dataset definition including studies and S3 keys

required

Returns:

Type Description
AdminDemoDatasetOut

The created/updated demo dataset with study and recording counts

Raises:

Type Description
ForbiddenError

If not an admin user

create_enrolment_token(pool_id) async

Create a single-use token a runner enrols into a pool with.

The token is in this answer only and is never shown again. Give it to the runner's installer, who passes it on standard input.

Parameters:

Name Type Description Default
pool_id UUID | str

The pool the runner joins.

required

Returns:

Type Description
EnrolmentTokenOut

The token and when it expires.

Raises:

Type Description
NotFoundError

If the pool does not exist

ForbiddenError

If not an admin user

create_user(user) async

Provision a new user.

Parameters:

Name Type Description Default
user AdminUserCreate

User creation data (email, password, subscription level, etc.)

required

Returns:

Type Description
AdminUserOut

The newly created user

Raises:

Type Description
ValidationError

If user data is invalid

ForbiddenError

If not an admin user

Example
new_user = await client.admin.create_user(
    AdminUserCreate(
        email="researcher@example.com",
        password="SecurePass123!",
        credits=100,
    )
)
print(f"Created user {new_user.id}")

decline_capability_request(request_id, decline) async

Decline a request for a capability, with the reason the organization is shown.

Parameters:

Name Type Description Default
request_id UUID | str

The request id.

required
decline AdminCapabilityRequestDecline

The reason.

required

decline_credit_request(request_id, decline) async

Decline a grant or extension request with a reason the requester is sent.

Parameters:

Name Type Description Default
request_id UUID | str

The request id.

required
decline AdminCreditRequestDecline

The reason, a note for administrators, and whether to email the requester.

required

delete_demo_dataset(dataset_id) async

Delete a demo dataset catalog entry. Does not delete S3 files.

Parameters:

Name Type Description Default
dataset_id UUID | str

UUID of the demo dataset to delete

required

Returns:

Type Description
MessageResponse

Message confirming deletion

Raises:

Type Description
NotFoundError

If dataset does not exist

ForbiddenError

If not an admin user

delete_project(project_id) async

Delete any account's project, with everything in it, in the background.

The deletion runs as the project's owner would run it and is written to the audit trail under your account. A second call while one is still running starts nothing new.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required

Returns:

Type Description
MessageResponse

Message saying the deletion started, or is already running

Raises:

Type Description
NotFoundError

If the project does not exist

ForbiddenError

If not an admin user

delete_user(user_id) async

Delete a user.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the user to delete

required

Returns:

Type Description
MessageResponse

Message confirming deletion

Raises:

Type Description
NotFoundError

If user does not exist

ForbiddenError

If not an admin, or if attempting to delete own account

Example
result = await client.admin.delete_user(user_id)
print(result.message)  # "User deleted successfully."

export_audit_events(filters=None, *, org_id=None, actor=None, email=None) async

Return the audit trail as CSV text, oldest first, under the same filters.

get_health() async

Read the API's health, dependency by dependency.

The public health check reports only the overall status; this is the breakdown behind it. The call answers even when a dependency is down, so read the fields rather than expecting an error.

Returns:

Type Description
AdminHealthDetail

The overall status and one field per dependency

Raises:

Type Description
ForbiddenError

If not an admin user

Example
health = await client.admin.get_health()
if health.status != "ok":
    print(health.postgres, health.redis, health.runners)

get_organization(org_id) async

Return one organization with its members, as the operators see it.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

Raises:

Type Description
NotFoundError

If the organization does not exist

ForbiddenError

If not an admin user

get_organization_credit_totals(org_id) async

Get an organization's all-time net credit movement per ledger reason.

Signed sums keyed by the CreditReason wire value (grants positive, charges negative): what the organization received and spent, by kind.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id

required

Returns:

Type Description
dict[str, int]

Mapping of reason -> net credits, e.g. {"grant": 300, "charge": -120}

Raises:

Type Description
NotFoundError

If the organization does not exist

ForbiddenError

If not an admin user

get_platform_stats() async

Get platform-wide statistics.

Returns:

Type Description
PlatformStats

Statistics including user counts, resource counts,

PlatformStats

and subscription level breakdown

Raises:

Type Description
ForbiddenError

If not an admin user

Example
stats = await client.admin.get_platform_stats()
print(f"Total users: {stats.total_users}")
print(f"Active: {stats.active_users}, Disabled: {stats.disabled_users}")
print(f"Recordings: {stats.total_recordings}")

get_storage_reconciliation() async

Read the last storage reconciliation report.

The reconciliation compares each data bucket's objects with the storage records that reference them, both ways. It runs on a schedule, and :meth:run_storage_reconciliation starts one now.

Returns:

Type Description
StorageReconciliationReport | None

The last report, or None when no report is stored: none has run

StorageReconciliationReport | None

yet, or the last one has expired

Raises:

Type Description
ForbiddenError

If not an admin user

Example
report = await client.admin.get_storage_reconciliation()
if report is not None and report.missing_total:
    for name, bucket in report.buckets.items():
        print(name, bucket.missing, bucket.missing_sample)

get_user(user_id) async

Get detailed information about a specific user.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the user to retrieve

required

Returns:

Type Description
AdminUserOut

The account's details, permissions and admin fields

Raises:

Type Description
NotFoundError

If user does not exist

ForbiddenError

If not an admin user

Example
user = await client.admin.get_user("550e8400-e29b-41d4-a716-446655440000")
print(f"{user.email}: disabled={user.disabled}")

get_user_organization(user_id) async

Return the organization an account belongs to, as that member sees it.

Every account belongs to exactly one organization; this is how an operator finds its id and the member's role before acting on it.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the account

required

Raises:

Type Description
NotFoundError

If the account does not exist

ForbiddenError

If not an admin user

get_user_usage(user_id) async

Get resource usage statistics for a specific user.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the user to retrieve usage for

required

Returns:

Type Description
ResourceUsage

Resource usage including projects, studies, recordings,

ResourceUsage

analyses, reports, analysis hours, and storage

Raises:

Type Description
NotFoundError

If user does not exist

ForbiddenError

If not an admin user

Example
usage = await client.admin.get_user_usage(user_id)
print(f"Projects: {usage.projects.active}/{usage.projects.all_time}")
print(f"Storage: {usage.storage.active} bytes")

grant_credits(org_id, grant) async

Grant an organization research credits directly.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
grant AdminGrantCreate

The amount, the term (none for a grant that never expires) and the reason, kept with the grant.

required

Returns:

Type Description
CreditGrant

The new grant.

list_analyses(*, status_any=None, user_id=None, user_email=None, org_id=None, analysis_config_id=None, failure_code=None, since=None, until=None, include_deleted=False, offset=0, limit=50) async

List analyses across every account, newest first.

The operator's view of the queue and of recent outcomes: each row is the analysis as its owner sees it plus the owner's account and email, the credits charged or held, and the process exit code. Filters combine.

Parameters:

Name Type Description Default
status_any list[AnalysisStatus] | None

Only analyses in at least one of these states

None
user_id UUID | str | None

Only analyses owned by this account

None
user_email str | None

Only analyses owned by the account with exactly this email

None
org_id UUID | str | None

Only analyses of this organization

None
analysis_config_id str | None

Only analyses of this configuration (string identity); an unknown identity matches nothing

None
failure_code ErrorCode | None

Only failed analyses with this failure code

None
since datetime | None

Only analyses enqueued at or after this time

None
until datetime | None

Only analyses enqueued before this time

None
include_deleted bool

Also list soft-deleted analyses, with their deletion time

False
offset int

Pagination offset (default: 0)

0
limit int

Maximum analyses to return (default: 50, max: 200)

50

Returns:

Type Description
AdminAnalysisListResponse

One page and the total the filters match

Raises:

Type Description
ForbiddenError

If not an admin user

Example
waiting = await client.admin.list_analyses(
    status_any=[AnalysisStatus.PENDING, AnalysisStatus.ENQUEUED]
)
for row in waiting.analyses:
    print(row.user_email, row.analysis_config_name, row.enqueue_time)

list_analysis_configs() async

List every registered analysis and who it is offered to.

Includes private analyses of every organization and withdrawn ones, each with the reason a promotion would be refused, if it would be.

Raises:

Type Description
ForbiddenError

If not an admin user

list_audit_events(filters=None, *, user_id=None, org_id=None, action=None, actor=None, email=None, since=None, until=None, offset=0, limit=50) async

Read the whole audit trail, newest first.

Every privileged or automated action on an account or its data, as recorded. Read-only: nothing writes to the trail through the API.

Parameters:

Name Type Description Default
filters AuditFilters | None

The views' filters (event types, time window, person, item)

None
user_id UUID | str | None

Only events concerning this account

None
org_id UUID | str | None

Only events concerning this organization

None
action str | None

Only events recording this action (the vocabulary: client.status.audit_actions())

None
actor str | None

Only events by exactly this actor, e.g. admin:<user id>

None
email str | None

Only events concerning the account with this email — the one it has now, or the one it had before it was deleted

None
since datetime | None

Only events at or after this time

None
until datetime | None

Only events before this time

None
offset int

Pagination offset (default: 0)

0
limit int

Maximum events to return (default: 50, max: 200)

50

Returns:

Type Description
AuditEventListResponse

One page of events and the total the filters match

Raises:

Type Description
ForbiddenError

If not an admin user

list_capability_requests(status=None, limit=50, offset=0) async

Return requests for capabilities across organizations.

Parameters:

Name Type Description Default
status CapabilityRequestStatus | None

Only requests in this state; all when omitted.

None
limit int

Maximum requests to return.

50
offset int

Requests to skip.

0

Returns:

Type Description
AdminCapabilityRequestPage

The page: pending requests first, oldest first.

list_compute_pools(*, offset=0, limit=50) async

List the compute pools, with the lease period and queue timeout in force.

The platform's own pool is named platform and has no organization.

Parameters:

Name Type Description Default
offset int

Pagination offset (default: 0).

0
limit int

Maximum pools to return (default: 50, max: 200).

50

Returns:

Type Description
ComputePoolListResponse

One page of pools, with the total.

Raises:

Type Description
ForbiddenError

If not an admin user

list_controls() async

Read every operator control.

For each: the configured default, the live override if any, and the effective value.

Returns:

Type Description
PlatformControlsOut

Every known control

Raises:

Type Description
ForbiddenError

If not an admin user

list_credit_requests(status=None, limit=50, offset=0) async

Return grant and extension requests across organizations.

Parameters:

Name Type Description Default
status CreditRequestStatus | None

Only requests in this state; all when omitted.

None
limit int

Maximum requests to return.

50
offset int

Requests to skip.

0

Returns:

Type Description
AdminCreditRequestPage

The page: pending requests first, oldest first.

list_demo_datasets() async

List all registered demo datasets.

Returns:

Type Description
list[AdminDemoDatasetOut]

List of demo datasets with study and recording counts

Raises:

Type Description
ForbiddenError

If not an admin user

list_organizations(*, search=None, include_retired=False, offset=0, limit=50) async

List organizations with their owner, seats, projects, storage and credits.

Parameters:

Name Type Description Default
search str | None

Filter by organization name or owner email substring (case-insensitive).

None
include_retired bool

Also list organizations that were retired (soft-deleted); off by default.

False
offset int

Pagination offset (default: 0).

0
limit int

Maximum organizations to return (default: 50, max: 200).

50

Returns:

Type Description
AdminOrganizationListResponse

One page of organizations, newest first, with the total.

Raises:

Type Description
ForbiddenError

If not an admin user

list_run_records(*, kind=None, analysis_id=None, runner_id=None, offset=0, limit=50) async

List where jobs ran and on what, as their runners attested it, newest first.

A record is written when the platform accepts a job's result, for every kind of job: its subject, the runner and pool, the runner's version, the image digest it ran, the inputs it read and the output it uploaded, its peak memory and its run length.

Parameters:

Name Type Description Default
kind LeaseKind | None

Only this kind of job's records.

None
analysis_id UUID | str | None

Only this analysis's records.

None
runner_id UUID | str | None

Only this runner's records.

None
offset int

Pagination offset (default: 0).

0
limit int

Maximum records to return (default: 50, max: 200).

50

Returns:

Type Description
RunRecordListResponse

One page of run records, with the total.

Raises:

Type Description
ForbiddenError

If not an admin user

list_runners(*, pool_id=None, offset=0, limit=50) async

List runners, newest first.

Parameters:

Name Type Description Default
pool_id UUID | str | None

Only this pool's runners.

None
offset int

Pagination offset (default: 0).

0
limit int

Maximum runners to return (default: 50, max: 200).

50

Returns:

Type Description
RunnerListResponse

One page of runners, with the total.

Raises:

Type Description
ForbiddenError

If not an admin user

list_site_exceptions(org_id, limit=50, offset=0) async

Return an organization's exception records, newest first.

An exception lets the organization run an analysis its configuration keeps on the platform at its site. Records only grow: a revocation is a record of its own, and the latest per analysis is its state.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
limit int

Maximum records to return.

50
offset int

Records to skip from the newest.

0

list_user_api_keys(user_id) async

List another account's API keys — never the secret.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the account

required

Returns:

Type Description
list[ApiKeyOut]

The account's keys, newest first

Raises:

Type Description
NotFoundError

If the account does not exist

ForbiddenError

If not an admin

list_users(*, search=None, disabled=None, pending=None, organization_id=None, offset=0, limit=50, sort=None) async

List users with optional filtering, ordering and pagination.

Parameters:

Name Type Description Default
search str | None

Filter by email substring (case-insensitive)

None
disabled bool | None

Filter by disabled status

None
pending bool | None

Filter by request-access approval. True returns only accounts awaiting approval (the approval queue).

None
organization_id UUID | str | None

Only the members of this organization.

None
offset int

Pagination offset (default: 0)

0
limit int

Maximum number of users to return (default: 50, max: 200)

50
sort AdminUserSort | None

Ordering — newest first by default, most recent sign-in first (never signed in last), or by email

None

Returns:

Type Description
AdminUserListResponse

Paginated user list with total count

Raises:

Type Description
ForbiddenError

If not an admin user

Example
# The approval queue
result = await client.admin.list_users(pending=True)
print(f"{result.total} accounts awaiting approval")

queue() async

Read the queue in one call.

Live counts by status, the oldest wait, and the accounts holding the most live analyses.

Returns:

Type Description
AdminQueueSummary

The queue summary, with the time it was sampled

Raises:

Type Description
ForbiddenError

If not an admin user

record_site_exception(org_id, exception) async

Let an organization run a platform-only analysis at its site, until an expiry.

It covers every version of the analysis. Recording one for an analysis that already has one in force replaces it.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
exception SiteExceptionCreate

The analysis, the agreement it rests on, and the expiry.

required

Returns:

Type Description
SiteExceptionOut

The record.

reject_user(user_id, rejection) async

Decline a pending access request and tell the applicant why.

The reason is chosen from a fixed list, and the applicant is emailed it in the platform's own wording unless notify is false. The reviewer's note is kept in the audit record and is never sent. The account is disabled at once and erased in the background, which frees the address, so an applicant declined for a fixable reason can apply again.

Only a pending account can be declined. An approved account is a conflict; disable or delete it with :meth:update_user or :meth:delete_user instead.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the pending user to decline

required
rejection AdminUserReject

The reason, the reviewer's note and whether to notify

required

Returns:

Type Description
AdminUserOut

The account as it stands at the moment of the decision

Raises:

Type Description
NotFoundError

If user does not exist

ForbiddenError

If not an admin user

YouSleepClientError

If the account has already been approved (409)

Example
from yousleep_common.models import AdminUserReject
from yousleep_common.types import RegistrationRejectionReason

await client.admin.reject_user(
    user_id,
    AdminUserReject(
        reason=RegistrationRejectionReason.PURPOSE_UNCLEAR,
        note="No description of the intended research.",
    ),
)

release_runner(runner_id) async

Release a quarantined runner, so it leases work again.

Parameters:

Name Type Description Default
runner_id UUID | str

The runner to release.

required

Returns:

Type Description
RunnerOut

The runner.

Raises:

Type Description
NotFoundError

If the runner does not exist

ForbiddenError

If not an admin user

revoke_runner(runner_id) async

Revoke a runner; it is refused from its next request on.

Parameters:

Name Type Description Default
runner_id UUID | str

The runner to revoke.

required

Returns:

Type Description
RunnerOut

The runner, now revoked.

Raises:

Type Description
NotFoundError

If the runner does not exist

ForbiddenError

If not an admin user

revoke_site_exception(org_id, analysis_config_id, revoke) async

End an organization's exception for an analysis before it expires.

Analyses already queued at the site are then failed when a runner would take them.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
analysis_config_id str

The analysis, by its machine-readable id.

required
revoke SiteExceptionRevoke

Why it ends.

required

Returns:

Type Description
SiteExceptionOut

The revocation record.

revoke_user_api_key(user_id, key_id) async

Revoke another account's API key; recorded against the administrator.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the account

required
key_id UUID | str

UUID of the key

required

Returns:

Type Description
MessageResponse

A confirmation message

Raises:

Type Description
NotFoundError

If the account or the key does not exist

ForbiddenError

If not an admin

run_storage_reconciliation() async

Start a storage reconciliation now, in the background.

Read the result with :meth:get_storage_reconciliation once it has finished.

Returns:

Type Description
MessageResponse

Message saying the reconciliation was queued

Raises:

Type Description
ForbiddenError

If not an admin user

set_control(name, update) async

Set a control's override, replacing any existing one.

The override applies immediately, must carry a reason, and must expire within the server's maximum override length; setting it again with a later expiry is how it is extended.

Parameters:

Name Type Description Default
name PlatformControlName | str

The control

required
update PlatformControlUpdate

The value, the reason and the expiry

required

Returns:

Type Description
PlatformControlOut

The control as it now reads

Raises:

Type Description
ValidationError

If the expiry lies outside the allowed window

ForbiddenError

If not an admin user

set_entitlement(org_id, capability, change) async

Turn one of an organization's capabilities on or off.

On needs the owner's acceptance of the latest published site agreement, unless the platform operates the organization. Off stops new work at the organization's site; nothing already there is touched.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
capability SiteCapability | str

The capability to change, or its value.

required
change AdminEntitlementChange

On or off, and why.

required

Returns:

Type Description
OrganizationCapabilities

The organization's capabilities after the change.

transfer_organization_ownership(org_id, request) async

Hand an organization to one of its members (administrator).

The recovery path when an organization's owner is unreachable. The current owner becomes an admin; both are told by email.

Parameters:

Name Type Description Default
org_id UUID | str

UUID of the organization

required
request AdminOwnershipTransfer

The member who becomes the owner

required

Raises:

Type Description
NotFoundError

If the organization or member does not exist

ForbiddenError

If not an admin user

update_analysis_config(analysis_config_id, update) async

Assign an analysis to an organization, promote it, or withdraw it.

Parameters:

Name Type Description Default
analysis_config_id str

The analysis configuration's id

required
update AdminAnalysisConfigUpdate

Only the fields provided change

required

Raises:

Type Description
NotFoundError

If no analysis has this id

YouSleepClientError

If the change is not allowed (409), with the reason

ForbiddenError

If not an admin user

Example
await client.admin.update_analysis_config(
    "yasa-sleep-staging-v1", AdminAnalysisConfigUpdate(owner_org_id=org.id)
)

update_organization(org_id, update) async

Set or clear an organization's limits and settings (only provided fields change).

The settings are whether the platform operates the organization, whether its analyses run at its site, and its own site-compute factor.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
update AdminOrganizationUpdate

The overrides to set; an explicit None clears one back to the configured default.

required

Returns:

Type Description
AdminOrganizationDetailOut

The organization with the limits now in force

Raises:

Type Description
NotFoundError

If the organization does not exist

ValidationError

If a study limit passes the platform's ceiling

ForbiddenError

If not an admin user

Example
org = await client.admin.update_organization(
    org_id, AdminOrganizationUpdate(study_limit=2000, seat_limit=None)
)
print(org.effective_study_limit, org.effective_seat_limit)

update_user(user_id, update) async

Update a user's properties.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the user to update

required
update AdminUserUpdate

Fields to update (only provided fields are changed)

required

Returns:

Type Description
AdminUserOut

The updated user

Raises:

Type Description
NotFoundError

If user does not exist

ForbiddenError

If not an admin, or if attempting self-demotion/self-disable

Example
updated = await client.admin.update_user(
    user_id,
    AdminUserUpdate(position="Sleep technologist"),
)
print(f"Updated: {updated.position}")

verify_user_email(user_id) async

Mark a user's email address as verified, in place of the emailed link.

Idempotent: verifying an already-verified address is a no-op.

Parameters:

Name Type Description Default
user_id UUID | str

UUID of the user

required

Returns:

Type Description
AdminUserOut

The user, with email_confirmed_at set

Raises:

Type Description
NotFoundError

If user does not exist

ForbiddenError

If not an admin user

AnalysesEndpoint

Bases: BaseEndpoint

Handles analysis-related API endpoints.

Analyses are sleep analysis jobs (staging, REM detection, etc.) run on recordings.

cancel(analysis_id) async

Cancel a running or pending analysis.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis to cancel

required

Returns:

Type Description
MessageResponse

Message confirming cancellation

Raises:

Type Description
NotFoundError

If analysis doesn't exist

ValidationError

If analysis cannot be cancelled

AuthenticationError

If not authenticated

count_for_project(project_id, *, status_any=None, analysis_config_id=None) async

Count the analyses a filter resolves to, without fetching them.

This is the expected_count a live-set analysis operation (e.g. a filtered batch delete) must present — the server re-resolves with the SAME predicate and rejects on drift.

delete(analysis_id) async

Delete an analysis and its results.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis to delete

required

Returns:

Type Description
MessageResponse

Message confirming deletion

Raises:

Type Description
NotFoundError

If analysis doesn't exist

AuthenticationError

If not authenticated

download_events_json(analysis_id) async

Download an analysis and its events as one document.

This is the downloadable events file, validated: the analysis as analysis and its events as events. The server records the download in the project's access log.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required

Returns:

Type Description
AnnotatedAnalysisEvents

The analysis and its events

Raises:

Type Description
NotFoundError

If analysis doesn't exist

AuthenticationError

If not authenticated

Example
document = await client.analyses.download_events_json(analysis.id)
print(document.analysis.status, len(document.events))

get(analysis_id) async

Get detailed information about an analysis.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required

Returns:

Type Description
AnalysisOut

Detailed analysis information

Raises:

Type Description
NotFoundError

If analysis doesn't exist

AuthenticationError

If not authenticated

get_biomarkers(analysis_id, soremp_threshold_min=15.0, statistics_window_start_ms=None, statistics_window_end_ms=None) async

Compute biomarkers for a completed staging analysis.

Returns 13 standard sleep biomarkers (TST, TIB, SE, SOL, WASO, etc.) plus quality metrics. The statistics window defaults to study lights off/on events, then full recording length.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required
soremp_threshold_min float

Max minutes from sleep onset to the first REM epoch for a SOREMP

15.0
statistics_window_start_ms int | None

Optional window start override (ms)

None
statistics_window_end_ms int | None

Optional window end override (ms)

None

Returns:

Type Description
AnalysisBiomarkersResponse

AnalysisBiomarkersResponse with biomarkers, QC metrics,

AnalysisBiomarkersResponse

metadata, and the server-defined presentation table

AnalysisBiomarkersResponse

(None when the server predates the field)

Raises:

Type Description
NotFoundError

If analysis doesn't exist

AuthenticationError

If not authenticated

get_config(analysis_config_id, *, expand_licence=False) async

Get detailed information about an analysis configuration.

Parameters:

Name Type Description Default
analysis_config_id str

ID of the analysis configuration

required
expand_licence bool

Return the licence with the licence document's name, validity dates and summary, not only its reference.

False

Returns:

Type Description
AnalysisConfigOut

Detailed analysis configuration information

Raises:

Type Description
NotFoundError

If configuration doesn't exist

AuthenticationError

If not authenticated

Example
config = await client.analyses.get_config("u-sleep-research-v1")
print(f"Name: {config.name}")
print(f"Description: {config.description}")

get_event_blocks(analysis_id) async

Get analysis results as one document of event blocks.

The bulk companion to :meth:get_events. A block is a run of consecutive equal-duration slots with per-slot attributes as parallel arrays, so a full night of 1 Hz sleep staging is a handful of blocks rather than tens of thousands of events. Prefer this whenever you are reading dense output at scale; get_events stays the convenient form for a single analysis you intend to iterate.

The document is returned whole — there is no server-side filtering, so fetch once and slice locally.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required

Returns:

Type Description
EventBlocksDocument

The analysis's events in block form.

Raises:

Type Description
NotFoundError

If the analysis doesn't exist or has no results

AuthenticationError

If not authenticated

Example
from yousleep_common.utils.event_blocks import blocks_to_events

document = await client.analyses.get_event_blocks(analysis.id)
print(document.event_count)
events = blocks_to_events(document.blocks)  # if you need them flat

get_events(analysis_id, *, window_start_time_ms=None, window_end_time_ms=None, window_mode=None, label=None, analysis_type=None, match_mode=None, offset=None, limit=None) async

Get analysis results as a list of events.

Called with the analysis id alone, this returns every event. The server can narrow the list in two independent stages:

  1. A time window, given in milliseconds from the start of the recording. window_mode decides how an event must sit in the window to be kept; the server's default is START.
  2. A label filter, which names one label, the labels of one analysis_type, or both. match_mode keeps the matching events (INCLUDE, the default) or removes them (EXCLUDE).

offset and limit page through what the filters leave, in the stored event order.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required
window_start_time_ms int | None

Start of the time window

None
window_end_time_ms int | None

End of the time window

None
window_mode WindowFilterModes | None

How an event is matched against the window

None
label EventLabel | None

One event label to match

None
analysis_type AnalysisType | None

Match every label of this analysis type

None
match_mode MatchModes | None

Keep or remove the events the labels match

None
offset int | None

Events to skip, after the filters

None
limit int | None

Maximum events to return

None

Returns:

Type Description
list[Event]

List of events from the analysis

Raises:

Type Description
NotFoundError

If analysis doesn't exist or has no results

ValidationError

If the window is invalid

AuthenticationError

If not authenticated

Example
from yousleep_common.types import AnalysisType

# The staging of the first hour only
events = await client.analyses.get_events(
    analysis.id,
    window_end_time_ms=3_600_000,
    analysis_type=AnalysisType.STAGING,
)

get_logs(analysis_id) async

Get analysis execution logs.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required

Returns:

Type Description
AnalysisLogs | None

Analysis logs, or None if no logs are available

Raises:

Type Description
NotFoundError

If analysis doesn't exist

AuthenticationError

If not authenticated

get_run(analysis_id) async

Get where an analysis runs or ran, and on what.

The place is known from submission; the image digest and the inputs' SHA-256 once the run's result is accepted. The runner's name is shown to the organization's owners and admins only.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required

Returns:

Type Description
AnalysisRunOut

The analysis's run

Raises:

Type Description
NotFoundError

If analysis doesn't exist

AuthenticationError

If not authenticated

list(recording_id, status=None, analysis_type=None, *, offset=None, limit=None) async

List analyses for a specific recording.

With neither offset nor limit every matching analysis is returned.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required
status AnalysisStatus | None

Optional analysis status to filter by

None
analysis_type AnalysisType | None

Optional analysis type to filter by

None
offset int | None

Analyses to skip

None
limit int | None

Maximum analyses to return (1-500)

None

Returns:

Type Description
list[AnalysisOut]

List of analyses

Raises:

Type Description
NotFoundError

If recording doesn't exist

AuthenticationError

If not authenticated

list_configs(*, expand_licence=False) async

List all available analysis configurations.

Parameters:

Name Type Description Default
expand_licence bool

Return each licence with the licence document's name, validity dates and summary, not only its reference.

False

Returns:

Type Description
list[AnalysisConfigOut]

List of available analysis configurations

Raises:

Type Description
AuthenticationError

If not authenticated

Example
configs = await client.analyses.list_configs()
for config in configs:
    print(f"{config.id}: {config.name}")

list_for_project(project_id, *, status_any=None, analysis_config_id=None, skip=0, limit=100) async

List one page of a project's analyses, newest first.

status_any and analysis_config_id are the analysis selector's filter vocabulary: this listing previews exactly what a live-set batch operation with the same filter will address. An unknown analysis_config_id raises NotFoundError rather than returning an empty page.

Example
failed = await client.analyses.list_for_project(
    project, status_any=[AnalysisStatus.FAILED]
)

list_publications() async

List the publications behind the analyses on offer to the caller.

The evidence of every analysis the caller may run, with the analysis families it is grouped by, as the portal's publications page shows it.

Returns:

Type Description
PublicationListing

The publications and their families.

quote(recording_id, request) async

Quote what an analysis would cost, without submitting it.

The quote runs the same validation and pricing as :meth:submit, so for the same request it equals the charge the submission would settle. It also says which balance pays for it (research analyses from grants, commercial ones from purchased credits) and whether that balance covers it. Nothing is created and no credits are reserved; the submission itself remains the gate.

A request that could not be submitted fails here in the same way, so a quote also checks a request before it is sent.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording to analyze

required
request AnalysisRequest

The same request you would pass to :meth:submit

required

Returns:

Type Description
BillingQuote

The estimated credits, the current balances and, when the

BillingQuote

submission would be refused, blocked_reason

Raises:

Type Description
ValidationError

If configuration or parameters are invalid

NotFoundError

If recording or configuration doesn't exist

AuthenticationError

If not authenticated

Example
quote = await client.analyses.quote(recording.id, request)
if quote.blocked_reason is None:
    analysis = await client.analyses.submit(recording.id, request)

stream_status(analysis_id, poll_interval=5.0, timeout=None) async

Stream analysis status updates until completion or timeout.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required
poll_interval float

Seconds between status checks

5.0
timeout float | None

Maximum total seconds to poll before raising ClientTimeoutError. None = no limit.

None

Yields:

Type Description
AsyncIterator[AnalysisOut]

Analysis status updates

Raises:

Type Description
NotFoundError

If analysis doesn't exist

AuthenticationError

If not authenticated

ClientTimeoutError

If timeout is reached before terminal status

Example
async for status in client.analyses.stream_status(
    analysis_id=analysis.id,
    timeout=3600,  # 1 hour max
):
    print(f"Status: {status.status}, Progress: {status.progress}%")
    if status.status == AnalysisStatus.COMPLETED:
        break

submit(recording_id, request) async

Submit a new analysis job.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording to analyze

required
request AnalysisRequest

Analysis request with config ID and optional parameters

required

Returns:

Type Description
AnalysisOut

Information about the submitted analysis

Raises:

Type Description
ValidationError

If configuration is invalid

NotFoundError

If recording doesn't exist

AuthenticationError

If not authenticated

Example
from yousleep_common.models import AnalysisRequest

# Submit with config ID
request = AnalysisRequest(
    analysis_config_id="u-sleep-research-v1",
    channels=["EEG Fpz-Cz", "EOG horizontal"],
    parameters=[["data-per-prediction", 3840]]
)
analysis = await client.analyses.submit(
    recording_id=recording.id,
    request=request
)

wait_for(analysis_id, poll_interval=5.0, timeout=None) async

Poll an analysis until it reaches a terminal status and return it.

Convenience wrapper over stream_status for the common case where only the final state matters: it consumes the status stream and returns the last (terminal) AnalysisOut. Terminal statuses are COMPLETED, FAILED, CANCELLED, REJECTED and REVIEW — inspect .status on the return value to distinguish success from failure.

Parameters:

Name Type Description Default
analysis_id UUID | str

UUID of the analysis

required
poll_interval float

Seconds between status checks

5.0
timeout float | None

Maximum total seconds to poll before raising ClientTimeoutError. None = no limit.

None

Returns:

Type Description
AnalysisOut

The analysis in its terminal status.

Raises:

Type Description
NotFoundError

If analysis doesn't exist

AuthenticationError

If not authenticated

ClientTimeoutError

If timeout is reached before a terminal status

Example
analysis = await client.analyses.wait_for(
    analysis_id=analysis.id,
    timeout=3600,
)
if analysis.status == AnalysisStatus.COMPLETED:
    events = await client.analyses.get_events(analysis.id)

AnalysisWorkflowResult dataclass

Everything an end-to-end analyze_file produced.

events/biomarkers/report are populated according to the fetch argument (biomarkers only when the analysis produced sleep staging).

analysis_id property

UUID of the completed analysis.

recording_id property

UUID of the uploaded recording.

AuthEndpoint

Bases: BaseEndpoint

Handles authentication-related API endpoints.

Provides methods for login, registration, token refresh, and user management.

login(credentials) async

Authenticate with email and password.

Parameters:

Name Type Description Default
credentials UserAuthentication

User authentication credentials (email and password)

required

Returns:

Type Description
TokenPair

Token information including access_token and refresh_token

Raises:

Type Description
AuthenticationError

If credentials are invalid

Example
from yousleep_common.models import UserAuthentication

credentials = UserAuthentication(
    email="user@example.com",
    password="secure_password"
)
tokens = await client.auth.login(credentials)

refresh(refresh_token) async

Refresh access token using refresh token.

The server reads the refresh token only from the bearer header, so this request sends it there in place of the client's own credential.

Parameters:

Name Type Description Default
refresh_token str

Valid refresh token

required

Returns:

Type Description
AccessToken

New access token information

Raises:

Type Description
AuthenticationError

If refresh token is invalid or expired

register(registration) async

Register a new user account.

Parameters:

Name Type Description Default
registration UserRegistration

User registration information

required

Returns:

Type Description
UserOut

Created user information

Raises:

Type Description
ValidationError

If registration data is invalid

Example
from yousleep_common.models import UserRegistration
from yousleep_common.types import UsageType

registration = UserRegistration(
    email="user@example.com",
    password="secure_password",
    name="Jane Ríos",
    organization="My Lab",  # or "Independent"
    usage_type=UsageType.RESEARCH,
    purpose="Sleep-staging research on our EEG cohort",
    position="Researcher",  # optional
)
user = await client.auth.register(registration)

request_password_reset(request) async

Request a password reset email.

The answer is the same whether or not an account with the address exists. No authentication is needed.

Parameters:

Name Type Description Default
request PasswordResetEmailRequest

The address of the account

required

Returns:

Type Description
MessageResponse

Message confirming the request

Example
from yousleep_common.models import PasswordResetEmailRequest

response = await client.auth.request_password_reset(
    PasswordResetEmailRequest(email="user@example.com")
)
print(response.message)

resend_confirmation_email(request) async

Resend the email confirmation link to an address.

The answer is the same whether or not an account with that address exists, or is already confirmed, so the call cannot be used to probe for accounts. No authentication is needed.

Parameters:

Name Type Description Default
request VerificationEmailRequest

The address of the account to confirm

required

Returns:

Type Description
MessageResponse

Message saying a link was sent if the account exists

Example
from yousleep_common.models import VerificationEmailRequest

response = await client.auth.resend_confirmation_email(
    VerificationEmailRequest(email="user@example.com")
)
print(response.message)

reset_password(token, request) async

Set a new password with the token from a reset or setup link.

Parameters:

Name Type Description Default
token str

Password reset token from the email

required
request ResetPasswordRequest

The new password. The server's password policy decides whether it is accepted.

required

Returns:

Type Description
MessageResponse

Message confirming the password change

Raises:

Type Description
NotFoundError

If the token is invalid, expired or already used

YouSleepClientError

With status 400 if the password does not meet the policy, or the account is disabled or unconfirmed

Example
from yousleep_common.models import ResetPasswordRequest

response = await client.auth.reset_password(
    "reset-token-from-email",
    ResetPasswordRequest(password="NewSecurePassword123!"),
)

verify_email(token) async

Verify email address using verification token.

Parameters:

Name Type Description Default
token str

Email verification token from email

required

Returns:

Type Description
UserOut

Verified user information

Raises:

Type Description
ValidationError

If token is invalid or expired

Example
user = await client.auth.verify_email(token="verification-token")
print(user.email)

BatchAnalysisWorkflowResult dataclass

Everything a multi-file analyze_files run produced.

Per-file failures never raise (mirroring the platform's 207-style batch semantics); inspect :attr:failed for files that did not complete.

failed property

Outcomes that failed or timed out.

succeeded property

Outcomes whose analysis completed successfully.

BatchEndpoint

Bases: BaseEndpoint

Handles batch operation API endpoints.

Batch endpoints allow bulk operations across multiple resources in a single request. All endpoints are project-scoped.

cancel_analyses(project_id, request) async

Batch cancel multiple analyses.

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project

required
request BatchCancelAnalysesRequest

Batch cancellation request with analysis IDs

required

Returns:

Type Description
BatchCancelAnalysesResponse

Response with per-item results and summary counts

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

create_studies(project_id, request) async

Batch create multiple studies in a project.

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project

required
request BatchCreateStudiesRequest

Batch study creation request with list of studies

required

Returns:

Type Description
BatchCreateStudiesResponse

Response with per-item results and summary counts

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

Example
from yousleep_common.models import BatchCreateStudiesRequest, BatchStudyItem

request = BatchCreateStudiesRequest(
    studies=[
        BatchStudyItem(name="Subject 001", age=35),
        BatchStudyItem(name="Subject 002", age=42),
    ]
)
result = await client.batch.create_studies(project_id, request)
print(f"Created: {result.summary.created}, Errors: {result.summary.errors}")

delete_analyses(project_id, request) async

Delete analyses by selector — e.g. clear failed runs by status.

Example
from yousleep_common.models import (
    AnalysisFilter, AnalysisSelector, DeleteAnalysesRequest,
)

accepted = await client.batch.delete_analyses(
    project_id,
    DeleteAnalysesRequest(
        selector=AnalysisSelector(
            filter=AnalysisFilter(status_any=["failed"])
        ),
        expected_count=3,
    ),
)

delete_recordings(project_id, request) async

Delete recordings by selector (ids | all); same contract.

delete_studies(project_id, request) async

Delete studies by selector (background cascade; returns on acceptance).

The selector addresses the set with exactly one mode — ids, filter or all. Live-set selectors (filter/all) must carry expected_count: the count previewed via the filtered study list; a drifted set is rejected with a conflict error.

Example
from yousleep_common.models import (
    DeleteStudiesRequest, StudyFilter, StudySelector,
)

# Delete every study tagged "pilot" (previewed as 12 studies).
accepted = await client.batch.delete_studies(
    project_id,
    DeleteStudiesRequest(
        selector=StudySelector(filter=StudyFilter(tags_any=["pilot"])),
        expected_count=12,
    ),
)
print(f"{accepted.count} studies scheduled for deletion")

edit_study_tags(project_id, request) async

Add/remove tags across the selected studies, set-based.

Example
from yousleep_common.models import (
    EditStudyTagsRequest, StudyFilter, StudySelector,
)

# Remove the tag "pilot" from every study carrying it.
result = await client.batch.edit_study_tags(
    project_id,
    EditStudyTagsRequest(
        selector=StudySelector(filter=StudyFilter(tags_any=["pilot"])),
        remove=["pilot"],
        expected_count=12,
    ),
)
print(f"{result.count} studies updated")

get_analyses_status(project_id, request) async

Batch poll analysis status for multiple analyses.

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project

required
request BatchGetAnalysesStatusRequest

Batch status request with analysis IDs

required

Returns:

Type Description
BatchGetAnalysesStatusResponse

Response with per-analysis status results and summary counts

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

get_event_blocks(project_id, request) async

Batch retrieve event blocks for multiple analyses.

The block-shaped counterpart to :meth:get_events: the same analyses, as one block document each rather than expanded event lists. Prefer it when reading dense per-epoch output across many analyses — the saving is in parse cost and object count (a night of staging decodes to one block rather than tens of thousands of events) rather than in compressed bytes.

There is no filter, matching the single-analysis block endpoint: a document is fetched once and sliced locally. Filtered access stays on :meth:get_events.

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project

required
request BatchGetEventBlocksRequest

Batch request with the analysis IDs

required

Returns:

Type Description
BatchGetEventBlocksResponse

Response with a block document per analysis and summary counts.

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

Example
from yousleep_common.models import BatchGetEventBlocksRequest
from yousleep_common.utils.event_blocks import blocks_to_events

response = await client.batch.get_event_blocks(
    project.id,
    BatchGetEventBlocksRequest(analysis_ids=[a.id, b.id]),
)
for entry in response.results.values():
    if entry.status == "ok" and entry.document is not None:
        events = blocks_to_events(entry.document.blocks)

get_events(project_id, request) async

Batch retrieve events for multiple analyses.

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project

required
request BatchGetEventsRequest

Batch event retrieval request with analysis IDs and optional filter

required

Returns:

Type Description
BatchGetEventsResponse

Response with per-analysis event results and summary counts

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

get_recordings_by_studies(project_id, request) async

Fetch the recordings of many studies in one request, keyed by study.

get_recordings_details(project_id, request) async

Batch fetch recording details for multiple recordings.

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project

required
request BatchGetRecordingsDetailsRequest

Batch details request with recording IDs

required

Returns:

Type Description
BatchGetRecordingsDetailsResponse

Response with per-recording detail results and summary counts

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

get_studies_by_ids(project_id, request) async

Fetch many studies by id in one request (per-item results).

patch_channels(project_id, request) async

Bulk-edit channel metadata (type/unit/name) across a project.

patch_studies_metadata(project_id, request) async

Apply subject-metadata patches (age/sex/BMI) across many studies.

Rows address studies by exact name or id. Field PRESENCE is the PATCH semantics — this method serializes with exclude_unset so an absent field leaves the study unchanged and an explicit None clears it.

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project.

required
request BatchPatchStudiesMetadataRequest

The metadata patch rows.

required

Returns:

Type Description
BatchPatchStudiesMetadataResponse

Per-item 207 results plus a summary.

quote_analyses(project_id, request) async

Pre-flight cost quote for a batch submission (same request body).

Runs the submission's own validation/pricing per (target, step) and returns per-step costs plus the batch total against the org balance. Advisory: the real submission's reserve remains the gate.

Parameters:

Name Type Description Default
project_id UUID | str

The project the targets belong to.

required
request BatchSubmitAnalysesRequest

The same pipeline/targets body you would submit.

required

Returns:

Type Description
BatchAnalysesQuote

BatchAnalysesQuote with per-step results, the total, and the

BatchAnalysesQuote

drain-order preview / blocked_reason for the batch total.

set_study_lights(project_id, request) async

Assign lights-off/on markers across many studies.

Each marker takes exactly one spelling: clock ("23:15", resolved per study with overnight wrap — only while the study's recording span is ≤ 24 h), datetime (read in the recording's wall-clock frame), or elapsed_ms. Existing markers win unless replace_existing; an explicit None deletes a marker (exclude_unset serialization, as for the metadata patch).

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project.

required
request BatchSetStudyLightsRequest

The lights assignment rows.

required

Returns:

Type Description
BatchSetStudyLightsResponse

Per-item 207 results (resolved instants echoed as elapsed ms).

submit_analyses(project_id, request) async

Batch submit analyses for multiple recordings with pipeline support.

Each pipeline step can depend on earlier steps via step_ids. If a step fails for a target, downstream steps auto-fail.

Parameters:

Name Type Description Default
project_id UUID | str

UUID or name of the project

required
request BatchSubmitAnalysesRequest

Batch analysis submission request with pipeline and targets

required

Returns:

Type Description
BatchSubmitAnalysesResponse

Response with per-item results and summary counts

Raises:

Type Description
NotFoundError

If project doesn't exist

ValidationError

If pipeline structure is invalid

AuthenticationError

If not authenticated

Example
from yousleep_common.models import (
    BatchSubmitAnalysesRequest,
    BatchAnalysisPipelineStep,
    BatchAnalysisTarget,
)

request = BatchSubmitAnalysesRequest(
    pipeline=[
        BatchAnalysisPipelineStep(
            step_id="staging",
            analysis_config_id="u-sleep-research-v1",
        ),
    ],
    targets=[
        BatchAnalysisTarget(study_id=study_id, recording_id=rec_id),
    ],
)
result = await client.batch.submit_analyses(project_id, request)

BillingEndpoint

Bases: BaseEndpoint

Handles billing and payment-related API endpoints.

Provides methods for the credit balance and credit purchases via Stripe.

create_checkout_session(request) async

Create a Stripe checkout session for one or more credit packages.

Parameters:

Name Type Description Default
request StripeCheckoutSessionCreate

The Stripe price ids to purchase (from :meth:credit_packages — each CreditPackage.price_id) and the optional redirect URLs. success_url is required when the server has no configured public app URL; both must point to an allowed application origin when an allowlist is configured.

required

Returns:

Type Description
StripeCheckoutSession

Stripe checkout session with a URL to complete payment.

Raises:

Type Description
ValidationError

If the price IDs or redirect URLs are invalid.

AuthenticationError

If not authenticated.

Example
from yousleep_common.models import StripeCheckoutSessionCreate

packages = await client.billing.credit_packages()
session = await client.billing.create_checkout_session(
    StripeCheckoutSessionCreate(
        price_ids=[packages[0].price_id],
        success_url="https://app.example.com/account",
        cancel_url="https://app.example.com/account",
    )
)
print(f"Complete payment at: {session.url}")

create_portal_session(request=None) async

Open the Stripe customer portal (purchase history, invoices, receipts).

Only meaningful after at least one purchase (a Stripe customer exists) — the server responds 409 otherwise.

Parameters:

Name Type Description Default
request StripePortalSessionCreate | None

Where Stripe sends the user when they leave the portal. Omit it to use Stripe's default.

None

Returns:

Type Description
StripePortalSession

The portal session; redirect the browser to its url.

Example
from yousleep_common.models import StripePortalSessionCreate

session = await client.billing.create_portal_session(
    StripePortalSessionCreate(return_url="https://app.example.com/account")
)
print(session.url)

credit_entries(limit=50, offset=0) async

Return a page of the org's credit ledger, newest first.

Every balance change is a ledger entry: grants, purchases, analysis charges, storage debits, what remained of a grant at its expiry and administrators' adjustments. An entry in the grants balance names its grant.

Parameters:

Name Type Description Default
limit int

Maximum entries to return (page size).

50
offset int

Entries to skip from the newest.

0

Returns:

Type Description
CreditLedgerPage

The page of entries plus the total entry count.

credit_packages() async

Get the purchasable credit packages (live Stripe prices).

Amounts come straight from Stripe, so what is displayed always matches what checkout charges. Pass a package's price_id to the checkout-session endpoint to purchase it.

Returns:

Type Description
list[CreditPackage]

The packages, sorted by credits ascending.

credits() async

Return the caller's organization's credits: research grants and purchased credits.

Research analyses are paid from grants, commercial analyses and storage above the free ceiling from purchased credits. Each balance reports what is held for running analyses and what is available.

Returns:

Type Description
OrgCredits

The organization's two balances.

Example
credits = await client.billing.credits()
print(f"Research grants available: {credits.grants.available}")

FileAnalysisOutcome dataclass

Outcome for a single input file of :meth:WorkflowsEndpoint.analyze_files.

status is "completed" when the file's analysis finished successfully (result is set), "failed" when any stage failed for this file (error describes it; analysis is attached when one was submitted), and "timeout" when the analysis was still running when the batch wait expired.

LegalEndpoint

Bases: BaseEndpoint

Handles legal document and license-related API endpoints.

Provides access to terms, privacy policies, and licenses.

commercial_license(version=None) async

Get the commercial license text.

Parameters:

Name Type Description Default
version int | None

The document version to return; the latest when omitted

None

Returns:

Type Description
LegalDocument

Commercial license document

Raises:

Type Description
AuthenticationError

If not authenticated

licenses(version=None) async

Get all available licenses.

Parameters:

Name Type Description Default
version int | None

The version to return for every licence; the latest when omitted

None

Returns:

Type Description
list[LegalDocumentShort]

List of available licenses (short form)

Raises:

Type Description
AuthenticationError

If not authenticated

non_commercial_license(version=None) async

Get the non-commercial license text.

Covers research, education, and personal use.

Parameters:

Name Type Description Default
version int | None

The document version to return; the latest when omitted

None

Returns:

Type Description
LegalDocument

Non-commercial license document

Raises:

Type Description
AuthenticationError

If not authenticated

privacy_policy_standard(version=None) async

Get the standard privacy policy.

Parameters:

Name Type Description Default
version int | None

The document version to return; the latest when omitted

None

Returns:

Type Description
LegalDocument

Privacy policy document for standard users

Raises:

Type Description
AuthenticationError

If not authenticated

site_agreement(version=None) async

Get the site agreement an organization's owner accepts before using its own hardware.

Parameters:

Name Type Description Default
version int | None

The document version to return; the latest when omitted

None

Returns:

Type Description
LegalDocument

The site agreement

Raises:

Type Description
NotFoundError

While no version is published, or for an unknown version

terms_and_conditions_standard(version=None) async

Get the standard terms and conditions.

Parameters:

Name Type Description Default
version int | None

The document version to return; the latest when omitted

None

Returns:

Type Description
LegalDocument

Terms and conditions document for standard users

Raises:

Type Description
AuthenticationError

If not authenticated

OrganizationsEndpoint

Bases: BaseEndpoint

Handles organization-related API endpoints.

Every account belongs to exactly one organization: the one created for it at sign-up, or the one it joined by invitation. These methods manage that organization and who belongs to it; client.user.get_organization returns it.

accept_invitation(request) async

Accept an invitation as the signed-in account and join the organization.

The account's verified email must match the invited address. The account's own organization of one is retired; what joining means is in the Terms and Conditions, which every account has accepted.

Parameters:

Name Type Description Default
request InvitationToken

The token from the invitation link.

required

Returns:

Type Description
UserOrganizationOut

The organization joined.

accept_site_agreement(org_id, version) async

Accept the site agreement on the organization's behalf.

The owner only, and only the latest published version (client.legal.site_agreement()). The acceptance is recorded and never changed; a later version is accepted anew.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
version int

The version read and accepted.

required

Returns:

Type Description
SiteAgreementAcceptance

The acceptance as recorded.

approve_runner(org_id, runner_id, fingerprint) async

Approve a pending runner of the organization's site pool.

Compare the fingerprint with the one the runner's host prints; the platform refuses an approval whose fingerprint differs.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
runner_id UUID | str

The runner to approve.

required
fingerprint str

The whole fingerprint, SHA256:….

required

create_enrolment_token(org_id) async

Create a single-use token a runner enrols into the organization's site pool with.

The first token creates the pool. Owners and admins, while the organization holds site compute. The token is in this answer only.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

Returns:

Type Description
EnrolmentTokenOut

The token, its pool, and when it expires.

create_invitation(org_id, request) async

Invite an email address into the organization (owner or admin).

The invitee's own organization must be empty when they accept: it is retired, and they join this one.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
request InvitationCreate

The address and the role to join with.

required

Returns:

Type Description
InvitationOut

The pending invitation. The token is only ever sent by email.

estimate_credits(org_id, request) async

Price a plan of analyses as its runs would be priced.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
request CreditEstimateRequest

The plan (studies, average recording length, analyses with their runs per study) and the margin to add.

required

Returns:

Type Description
CreditEstimate

The cost per analysis, the total, and the total with the margin.

export_audit(org_id, filters=None) async

Return the same events as CSV text, oldest first.

get(org_id) async

Get one of the caller's organizations.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

Returns:

Type Description
UserOrganizationOut

The organization as the caller sees it.

Raises:

Type Description
NotFoundError

If the caller is not a member of it.

get_capabilities(org_id) async

Return the organization's capabilities on its own hardware.

Running analyses on its own hardware and keeping recordings at its site are each off until a platform administrator turns them on. The answer also says which version of the site agreement is published and which the organization accepted. Any member.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

get_compute_pool(org_id) async

Return the organization's site pool, or None before it has one.

An organization has at most one site pool, created with its first enrolment token. Owners and admins.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

get_usage(org_id) async

Return the organization's usage against its ceilings, member by member.

Owner and admins only. Storage and projects against their ceilings, then one row per member: the bytes and projects they hold (private ones included, without naming them), their analyses and the credits those cost.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

Returns:

Type Description
OrganizationUsageOut

The organization's totals and one row per member, largest first.

list_audit(org_id, filters=None, *, offset=0, limit=50) async

Return the organisation's audit trail (its owner and admins).

Its members, invitations and roles, its storage and credits, and every event about a project the organisation could see when it happened. Newest first; filters narrows by event types, a time window, a person or an item.

Returns:

Type Description
AuditEventListResponse

One page of events (limit at most 200) and the total.

list_capability_requests(org_id, limit=50, offset=0) async

Return the organization's requests for capabilities, newest first.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
limit int

Maximum requests to return.

50
offset int

Requests to skip from the newest.

0

list_credit_requests(org_id, limit=50, offset=0) async

Return the organization's grant and extension requests, newest first.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
limit int

Maximum requests to return.

50
offset int

Requests to skip from the newest.

0

list_grants(org_id) async

Return the organization's research grants, soonest to expire first.

Each with what remains of it; the starter grant, which never expires, comes last.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

list_invitations(org_id) async

List an organization's open invitations: pending, and recently expired.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

Returns:

Type Description
list[InvitationOut]

The invitations, newest first.

list_members(org_id) async

List an organization's members.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required

Returns:

Type Description
list[MemberOut]

The members with their roles, owner first.

list_runners(org_id, *, offset=0, limit=50) async

List the runners of the organization's site pool, newest first.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
offset int

Pagination offset (default: 0).

0
limit int

Maximum runners to return (default: 50, max: 200).

50

lookup_invitation(request) async

Describe the invitation behind a link, before accepting it.

No authentication is required: the token itself is the credential.

Parameters:

Name Type Description Default
request InvitationToken

The token from the invitation link.

required

Returns:

Type Description
InvitationPreview

The organization, inviter and invited address.

Raises:

Type Description
NotFoundError

If the token is unknown, expired or revoked.

release_runner(org_id, runner_id) async

Release a quarantined runner of the organization's site pool.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
runner_id UUID | str

The runner to release.

required

remove_member(membership_id) async

Remove a member from the organization, or leave it by removing yourself.

Projects the member owned pass to the organization's owner, and the member receives a fresh organization of their own. The owner cannot be removed; ownership is transferred first.

Parameters:

Name Type Description Default
membership_id UUID | str

The membership to end. The caller's own membership id is on UserOrganizationOut.membership_id.

required

Returns:

Type Description
MessageResponse

A message confirming the removal.

request_capability(org_id, request) async

Ask the platform's administrators to turn a capability on.

The owner only, one request per capability at a time, and none for a capability that is already on.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
request CapabilityRequestCreate

The capability, and what it is for.

required

Returns:

Type Description
CapabilityRequestOut

The pending request.

request_extension(org_id, grant_id, request) async

Ask for more time on a grant before it expires.

Owner and admins only.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
grant_id UUID | str

The grant to extend.

required
request ExtensionRequestCreate

The extra time asked for, and why.

required

Returns:

Type Description
CreditRequestOut

The pending request.

request_grant(org_id, request) async

Ask for a research grant for a planned piece of work.

Owner and admins only. An administrator reviews the plan, the computed cost and the amount asked for, and approves or declines it; the requester is emailed the decision.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
request GrantRequestCreate

The plan, the margin, the amount asked for, the term and what the research is.

required

Returns:

Type Description
CreditRequestOut

The pending request.

revoke_invitation(invitation_id) async

Withdraw a pending invitation (owner or admin).

Parameters:

Name Type Description Default
invitation_id UUID | str

The invitation id.

required

Returns:

Type Description
MessageResponse

A message confirming the revocation.

revoke_runner(org_id, runner_id) async

Revoke a runner of the organization's site pool; it is refused from its next request on.

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
runner_id UUID | str

The runner to revoke.

required

transfer_ownership(org_id, request) async

Hand the organization to another member (owner only).

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
request OwnershipTransfer

The new owner and the caller's password.

required

Returns:

Type Description
UserOrganizationOut

The organization, with the caller's new role (admin).

update(org_id, request) async

Rename an organization (owner or admin).

Parameters:

Name Type Description Default
org_id UUID | str

The organization id.

required
request OrganizationUpdate

The new name.

required

Returns:

Type Description
UserOrganizationOut

The updated organization.

update_member_role(membership_id, request) async

Change a member's role between admin and member.

Parameters:

Name Type Description Default
membership_id UUID | str

The membership to change (from list_members).

required
request MemberRoleUpdate

The new role.

required

Returns:

Type Description
MemberOut

The updated member.

ProjectsEndpoint

Bases: BaseEndpoint

Handles project-related API endpoints.

Projects are the top-level organizational unit in YouSleep.

bucket_files(project_id, prefix='') async

Return every file under prefix in a bucket project's bucket that can be registered where it is.

The platform signs each page of the listing, and this client sends it to the bucket, which answers only where the organisation's network reaches it. Files under youSleep/, where youSleep writes, and files that are not EDF files are left out. Register each with client.recordings.register_in_place.

Parameters:

Name Type Description Default
project_id UUID | str

A project whose recordings are in its organisation's bucket.

required
prefix str

The folder to list, as its keys start; empty lists the whole bucket.

''

Returns:

Type Description
list[BucketFile]

The files, with their keys and sizes.

Raises:

Type Description
ClientConnectionError

If the bucket does not answer, or refuses.

YouSleepClientError

If the project's recordings are not in its organisation's bucket (project_not_at_site).

bucket_folders(project_id) async

Return the folders a bucket project registered files from: where to look for new files.

Parameters:

Name Type Description Default
project_id UUID | str

A project whose recordings are in its organisation's bucket.

required

Returns:

Type Description
BucketFolders

Each folder, ending in /, or empty for the whole bucket; a

BucketFolders

folder inside another is left out.

create(project) async

Create a new project.

Parameters:

Name Type Description Default
project ProjectCreate

Project data (name is required; description, tags optional)

required

Returns:

Type Description
ProjectOut

Created project information

Raises:

Type Description
ValidationError

If project data is invalid

AuthenticationError

If not authenticated

Example
from yousleep_common.models import ProjectCreate

project = ProjectCreate(
    name="My Study",
    description="Sleep analysis project",
)
created = await client.projects.create(project)

delete(project_id) async

Delete a project.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project to delete

required

Returns:

Type Description
MessageResponse

Message confirming deletion

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

export_audit(project_id, filters=None) async

Return the same events as CSV text, oldest first.

get(project_id) async

Get a specific project by ID.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required

Returns:

Type Description
ProjectOut

Project information

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

get_report(project_id, by_group=False, groups=None, analysis_configs=None, stratify_by=None, stratify_edges=None, longitudinal=False, longitudinal_basis=None, calendar_granularity='month', subjects=None, include_biomarkers=False, soremp_threshold_min=15.0, statistics_window_start_ms=None, statistics_window_end_ms=None) async

Get a report for the project.

Aggregates event statistics across all completed analyses in every study of the project. The report can optionally be grouped by study tags, computed longitudinally, and carry aggregated biomarker summary statistics (with their tables and charts).

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required
by_group bool

Group the statistics by study tags. Off by default — note that :class:~yousleep_common.models.ReportExportProjectParams defaults by_group ON, so omitting it there means the opposite of omitting it here

False
groups list[str] | None

With by_group, only these study tags form the report's comparison groups; studies matching none are excluded (with a warning). Default: every tag becomes a group.

None
analysis_configs list[str] | None

Only these analysis configurations contribute; default: every completed analysis does. The project's own vocabulary comes from list_analysis_configs.

None
stratify_by MetadataType | None

Group the statistics by a subject-metadata factor (age, sex or BMI) instead of tags. Each study lands in exactly one stratum; studies missing the factor form a "Not set" stratum. Mutually exclusive with by_group, groups and longitudinal. The response's stratification block echoes the resolved bins and per-stratum study counts.

None
stratify_edges list[float] | None

Bin edges overriding the server's configured defaults; numeric factors (age, bmi) only. Strictly increasing; bins are half-open intervals [a, b).

None
longitudinal bool

Compute the longitudinal section — the flat boxplot with time bins as rows, one chart per metric

False
longitudinal_basis LongitudinalBasis | None

What a bin is: "session-index" (a study's position within its subject's series) or "calendar" (a week or month of recording starts). None lets the server choose: session-index when any study in scope carries a subject key, else calendar

None
calendar_granularity CalendarGranularity

"day", "week" or "month" — the span of one calendar bin; ignored on a session-index basis

'month'
subjects list[str] | None

Draw these subjects as lines beside the bins, one series each; keys are compared case- and whitespace-insensitively

None
include_biomarkers bool

Whether to compute biomarkers for staging analyses

False
soremp_threshold_min float

Max minutes from sleep onset to the first REM epoch for a SOREMP

15.0
statistics_window_start_ms int | None

Optional window start override (ms); must be given together with the end

None
statistics_window_end_ms int | None

Optional window end override (ms); must be given together with the start

None

Returns:

Type Description
ProjectReportResponse

Project report envelope with aggregated event stats and optional

ProjectReportResponse

biomarker summaries across all studies in the project. Large

ProjectReportResponse

reports carry shared per-study origins lists once, referenced per

ProjectReportResponse

entry — read an entry's origins through

ProjectReportResponse

report.resolve_origins(entry) rather than entry.origins

ProjectReportResponse

directly.

Raises:

Type Description
NotFoundError

If project doesn't exist

ValidationError

If the requested report mode is invalid — groups without by_group, by_group together with longitudinal, or only one end of the statistics window

AuthenticationError

If not authenticated

list(*, visibility=None, name_contains=None, skip=None, limit=None, order=None) async

List the projects the authenticated user can open.

Projects are ordered by creation time, oldest first unless order is "desc". With neither skip nor limit every project is returned.

Parameters:

Name Type Description Default
visibility Visibility | None

Only projects with this visibility (private, org or shared), among the accessible ones — a filter, never a widening.

None
name_contains str | None

Only projects whose name contains this text (case-insensitive).

None
skip int | None

Projects to skip

None
limit int | None

Maximum projects to return (1-500)

None
order Literal['asc', 'desc'] | None

"asc" (the default) or "desc" for newest first

None

Returns:

Type Description
list[ProjectOut]

List of projects

Raises:

Type Description
AuthenticationError

If not authenticated

Example
recent = await client.projects.list(order="desc", limit=5)

list_analysis_configs(project_id) async

List the analysis configurations the project has results from.

The project's own vocabulary, with how much each configuration produced — what a report's analysis_configs selection can name, which the report itself cannot say once a selection narrows it.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required

Returns:

Type Description
list[ProjectAnalysisConfig]

One entry per configuration with completed analyses, by name.

Raises:

Type Description
NotFoundError

If the project doesn't exist

AuthenticationError

If not authenticated

list_audit(project_id, filters=None, *, offset=0, limit=50) async

Return a project's audit trail (the project's owner).

Every event about the project, whoever caused it. Newest first; filters narrows by event types, a time window, a person or an item.

Returns:

Type Description
AuditEventListResponse

One page of events (limit at most 200) and the total.

list_demo_datasets() async

List available public demo datasets.

Returns:

Type Description
list[DemoDatasetDescription]

List of demo dataset descriptions

Raises:

Type Description
AuthenticationError

If not authenticated

list_shares(project_id) async

List the members a shared project is open to (project owner only).

Parameters:

Name Type Description Default
project_id UUID | str

The project id.

required

Returns:

Type Description
list[ProjectShareOut]

The shares, one per member.

new_bucket_files(project_id) async

Look for new files: the files a bucket project has not registered, in the folders it registered files from.

Each folder is listed again through requests the platform signs, as bucket_files lists it, and what the listing found is reported. A folder reported whole is judged too: a recording whose file is gone from its key, or changed there, is missing at the site (RecordingOut.missing_since), and one whose file is back is not. Register each new file with client.recordings.register_in_place, naming its folder.

Parameters:

Name Type Description Default
project_id UUID | str

A project whose recordings are in its organisation's bucket.

required

Returns:

Type Description
dict[str, list[BucketFile]]

Each folder's new files.

Raises:

Type Description
ClientConnectionError

If the bucket does not answer, or refuses.

Example
found = await client.projects.new_bucket_files(project.id)
for folder, files in found.items():
    for file in files:
        await client.recordings.register_in_place(
            project.id, file.key, folder=folder
        )

register_demo_dataset(dataset, project_name) async

Register a public demo dataset as a new project.

Parameters:

Name Type Description Default
dataset str

Name of the demo dataset to register (e.g. "sleep-edfx")

required
project_name str

Name for the created project

required

Returns:

Type Description
ProjectOut

Created project with demo data

Raises:

Type Description
NotFoundError

If dataset doesn't exist

AuthenticationError

If not authenticated

report_found(project_id, files, *, folder=None) async

Report files a listing of a bucket project's bucket found; learn which are new.

Parameters:

Name Type Description Default
project_id UUID | str

A project whose recordings are in its organisation's bucket.

required
files list[BucketFile]

Up to 5,000 files, as bucket_files lists them.

required
folder str | None

The folder files lists in full. The project's files registered from below it that the listing lacks, or names changed, are then missing at the site, and those it names are there again.

None

Returns:

Type Description
BucketFoundOut

The keys of those the project can register and has not

BucketFoundOut

registered, and of those missing at the site.

share(project_id, request) async

Share a project with one member of its organization (project owner only).

The project's visibility must be shared; set it with update.

Parameters:

Name Type Description Default
project_id UUID | str

The project id.

required
request ProjectShareCreate

The member to share with.

required

Returns:

Type Description
ProjectShareOut

The share granted.

sign_bucket_listing(project_id, listing) async

Sign one page of a bucket project's bucket listing, for this client to send.

bucket_files sends every page and reads them.

Parameters:

Name Type Description Default
project_id UUID | str

A project whose recordings are in its organisation's bucket.

required
listing BucketListing

The prefix, whether to list folders, and the page's token.

required

Returns:

Type Description
SignedRequest

The bucket's GET, valid for a few minutes.

unshare(share_id) async

Withdraw a share (project owner only).

Parameters:

Name Type Description Default
share_id UUID | str

The share id, from list_shares.

required

Returns:

Type Description
MessageResponse

A message confirming the withdrawal.

update(project_id, project) async

Update an existing project (PATCH).

Universal field semantics:

  • Omit a field to leave it unchanged.
  • Include a field with a value to update it.
  • Include a field with None to clear it (where nullable in storage — description, tags).

name cannot be cleared; sending name=None raises a validation error. Omit name to leave it unchanged.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project to update

required
project ProjectUpdate

Partial update (only included fields are modified)

required

Returns:

Type Description
ProjectOut

Updated project information

Raises:

Type Description
NotFoundError

If project doesn't exist

ValidationError

If update data is invalid (e.g., name=None)

AuthenticationError

If not authenticated

Example
from yousleep_common.models import ProjectUpdate

# Clear the description, leave everything else alone
project_update = ProjectUpdate(description=None)
updated = await client.projects.update(project_id, project_update)

RecordingsEndpoint

Bases: BaseEndpoint

Handles recording-related API endpoints.

Recordings are sleep study files (EDF, EDF+, etc.) associated with studies.

__init__(client)

Hold the parent client, and the policies of the studies uploaded to.

add_in_place(project_id, key, header_bytes, full_file_size, filename, *, folder=None) async

Add the bucket's file at key as a recording, from its cleaned header.

register_in_place reads and cleans the header first; send only a header cleaned by the project's rule.

Parameters:

Name Type Description Default
project_id UUID | str

A project whose recordings are in its organisation's bucket.

required
key str

The file's key in the bucket.

required
header_bytes bytes

The file's EDF header, cleaned by the project's rule.

required
full_file_size int

The file's size in bytes.

required
filename str

The name the recording carries; its study takes it without its extension.

required
folder str | None

The folder the file was chosen from; the file's own folder if not given.

None

Returns:

Type Description
RecordingOut

The registered recording.

delete(recording_id) async

Delete a recording.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording to delete

required

Returns:

Type Description
MessageResponse

Message confirming deletion

Raises:

Type Description
NotFoundError

If recording doesn't exist

AuthenticationError

If not authenticated

download_url(recording_id) async

Get a pre-signed download URL for a recording file.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required

Returns:

Type Description
PreSignedDownloadUrl

Pre-signed download URL with expiry information

Raises:

Type Description
NotFoundError

If recording doesn't exist

AuthenticationError

If not authenticated

finalize(recording_id) async

Finalize a recording after upload.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required

Returns:

Type Description
RecordingOut

Finalized recording information

Raises:

Type Description
NotFoundError

If recording doesn't exist

ValidationError

If recording cannot be finalized

AuthenticationError

If not authenticated

get(recording_id) async

Get detailed information about a recording.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required

Returns:

Type Description
RecordingOut

Detailed recording information including header data

Raises:

Type Description
NotFoundError

If recording doesn't exist

AuthenticationError

If not authenticated

get_channel(recording_id, channel_index) async

Get a specific channel by index.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required
channel_index int

Index of the channel

required

Returns:

Type Description
ChannelOut

Channel information

Raises:

Type Description
NotFoundError

If recording or channel doesn't exist

AuthenticationError

If not authenticated

Example
channel = await client.recordings.get_channel(
    recording_id=recording.id,
    channel_index=0
)
print(f"{channel.name} ({channel.type}): {channel.sample_rate} Hz")

get_events(recording_id) async

Get all events for a recording across all analyses.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required

Returns:

Type Description
list[Event]

List of events from all analyses on this recording

Raises:

Type Description
NotFoundError

If recording doesn't exist

AuthenticationError

If not authenticated

list(study_id, *, offset=None, limit=None) async

List the recordings of a study.

With neither offset nor limit every recording is returned.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required
offset int | None

Recordings to skip

None
limit int | None

Maximum recordings to return (1-500)

None

Returns:

Type Description
list[RecordingOut]

List of recordings

Raises:

Type Description
NotFoundError

If study doesn't exist

AuthenticationError

If not authenticated

list_channels(recording_id) async

List all channels in a recording.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required

Returns:

Type Description
list[ChannelOut]

List of channels with metadata

Raises:

Type Description
NotFoundError

If recording doesn't exist

AuthenticationError

If not authenticated

Example
channels = await client.recordings.list_channels(
    recording_id=recording.id
)
for channel in channels:
    print(f"{channel.name} ({channel.type}): {channel.sample_rate} Hz")

list_for_project(project_id, *, offset=0, limit=50) async

List one page of a project's recordings across all of its studies.

Each recording carries its study's id and name and a summary of its analyses, so a project overview needs no request per study.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required
offset int

Recordings to skip (default: 0)

0
limit int

Maximum recordings to return (default: 50, max: 200)

50

Returns:

Type Description
PaginatedRecordingsResponse

One page of recordings with the total across all pages

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

Example
page = await client.recordings.list_for_project(project.id, limit=200)
for recording in page.recordings:
    print(recording.study_name, recording.original_filename)
print(f"{page.total} recordings in the project")

pre_register(study_id, header_bytes, full_file_size, filename) async

Pre-register a recording before upload.

Sends the EDF file header for server-side validation and receives a presigned PUT URL for the file.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required
header_bytes bytes

EDF header bytes (extracted from the file)

required
full_file_size int

Total size of the file in bytes

required
filename str

Name of the recording file

required

Returns:

Type Description
PreRegistrationResponse

Pre-registration response with recording info and presigned PUT URL

Raises:

Type Description
NotFoundError

If study doesn't exist

AuthenticationError

If not authenticated

register_in_place(project_id, key, *, name=None, folder=None) async

Register a file a bucket project's bucket already stores, where it is.

Nothing is uploaded or copied: the file stays in the organisation's bucket, and deleting its recording only unregisters it. It: 1. Reads the file's header through a ranged GET the platform signs; the bucket answers only where the organisation's network reaches it, so run this from there. 2. Applies the project's pseudonymisation rule to the header, as an upload's is cleaned. The file in the bucket is not changed. 3. Registers the file with the cleaned header and its size, in a study of its own named after the file.

Parameters:

Name Type Description Default
project_id UUID | str

A project whose recordings are in its organisation's bucket.

required
key str

The file's key in the bucket, as client.projects.bucket_files lists it.

required
name str | None

The name the recording and its study carry; the file's own name if not given. A project that codes file names requires it: pass the code your own records map to the file.

None
folder str | None

The folder the file was chosen from, ending in /, or empty for the whole bucket: client.projects.new_bucket_files looks there again. The file's own folder if not given.

None

Returns:

Type Description
RecordingOut

The registered recording.

Raises:

Type Description
ValueError

If the file does not start with an EDF header, or the project codes file names and no name is given

ClientConnectionError

If the bucket does not answer, or refuses.

ValidationError

If the file cannot be registered: it is under youSleep/ or not an EDF file (key_not_registrable).

YouSleepClientError

If the project already has the file (already_registered).

Example
for file in await client.projects.bucket_files(project.id, "nights/"):
    await client.recordings.register_in_place(project.id, file.key)

set_channel_identity(recording_id, channel_index, patch) async

Set or clear what a channel is: the side it records and its canonical name.

A field included with a value sets it, a field included as None clears it, and a field left out keeps its value. A value set here is recorded as a person's correction, and re-reading the file's label never overwrites it.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required
channel_index int

Index of the channel

required
patch ChannelIdentityPatch

The correction (at least one field must be included)

required

Returns:

Type Description
ChannelOut

The channel as it now stands

Raises:

Type Description
NotFoundError

If recording or channel doesn't exist

ValidationError

If the canonical name or side is invalid

AuthenticationError

If not authenticated

Example
from yousleep_common.models import ChannelIdentityPatch

channel = await client.recordings.set_channel_identity(
    recording.id,
    channel_index=2,
    patch=ChannelIdentityPatch(laterality="left", canonical_name="E1"),
)
print(channel.laterality, channel.identity_source)

sign_bucket_reads(project_id, keys) async

Sign a ranged GET of each file's header in a bucket project's bucket.

register_in_place sends it, cleans the header and registers the file.

Parameters:

Name Type Description Default
project_id UUID | str

A project whose recordings are in its organisation's bucket.

required
keys list[str]

The files' keys, at most 100.

required

Returns:

Type Description
BucketReadsOut

Each key's GET of its first 64 KiB, valid for a few minutes.

update_channel_type(recording_id, channel_index, type) async

Update the type of a specific channel.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required
channel_index int

Index of the channel

required
type str

New channel type

required

Returns:

Type Description
MessageResponse

Message confirming update

Raises:

Type Description
NotFoundError

If recording or channel doesn't exist

ValidationError

If channel type is invalid

AuthenticationError

If not authenticated

update_channel_unit(recording_id, channel_index, unit) async

Update the unit of a specific channel.

Parameters:

Name Type Description Default
recording_id UUID | str

UUID of the recording

required
channel_index int

Index of the channel

required
unit str

New unit

required

Returns:

Type Description
MessageResponse

Message confirming update

Raises:

Type Description
NotFoundError

If recording or channel doesn't exist

ValidationError

If unit is invalid

AuthenticationError

If not authenticated

upload(study_id, file, on_progress=None, *, name=None) async

Upload a recording file, pseudonymised by its project's policy.

It: 1. Applies the pseudonymisation rule to the file's EDF header: the patient and recording identification fields are blanked, and the start date becomes EDF+'s unknown date if the project removes dates. Your file is not changed; only the uploaded copy is. 2. Pre-registers the recording with the API (sends the cleaned header) 3. Uploads the file, with the cleaned header, via presigned PUT 4. Finalizes the recording

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required
file BinaryIO | Path

File-like object or path to recording file

required
on_progress Callable[[int], None] | None

Optional callback for upload progress (percentage 0-100)

None
name str | None

The name the recording carries on the platform; the file's own name if not given. A project that codes file names requires it: pass the code your own records map to the file. The file's extension is added if the name has none.

None

Returns:

Type Description
RecordingOut

Information about the uploaded recording

Raises:

Type Description
ValueError

If the file does not start with an EDF header, or the project codes file names and no name is given

ValidationError

If file is invalid or unsupported format

NotFoundError

If study doesn't exist

AuthenticationError

If not authenticated

Example
recording = await client.recordings.upload(
    study_id=study.id,
    file=Path("recording.edf"),
)

ReportExportsEndpoint

Bases: BaseEndpoint

Handles report export API endpoints.

An export renders a study or project report server-side into a downloadable artifact (a branded PDF, or a markdown bundle). Exports are created against the project or study whose report they render, then polled and downloaded through the user-scoped export lifecycle:

import asyncio

from yousleep_common.models import ReportExportCreate

export = await client.report_exports.create_for_study(
    study_id=study.id,
    export=ReportExportCreate(format="pdf"),
)
while export.status in ("queued", "running"):
    await asyncio.sleep(2)
    export = await client.report_exports.get(export.id)
print(export.download_url)

Creation is idempotent: an identical export that is still live is returned as-is rather than re-rendered, so a fresh create can already be completed with its download_url set. Check status before polling — see the note on each create method.

create_for_project(project_id, export) async

Create an export of a project's report.

The export renders asynchronously: the returned record is normally queued, and :meth:get is the poll target for its status and download URL. Creation is idempotent — when an identical export is still live, that existing export is returned instead of a new one, and it comes back already completed with its download_url set. Check status before polling rather than polling unconditionally.

The artifact carries the audit appendix (which studies and analyses the numbers came from) and expires after a few days, at which point status becomes expired and the download URL is withheld.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required
export ReportExportCreate

Export request (format, optional section layout, optional project report mode via project_params). Omitting project_params — or omitting its by_group — renders the report BY GROUP over every tag, unlike :meth:ProjectsEndpoint.get_report, which is flat by default

required

Returns:

Type Description
ReportExportOut

The export record — queued, or an identical live export already

ReportExportOut

completed with its download URL

Raises:

Type Description
NotFoundError

If project doesn't exist

ValidationError

If the export request is invalid

AuthenticationError

If not authenticated

Example
from yousleep_common.models import (
    ReportExportCreate,
    ReportExportProjectParams,
)

export = await client.report_exports.create_for_project(
    project_id=project.id,
    export=ReportExportCreate(
        format="pdf",
        project_params=ReportExportProjectParams(by_group=True),
    ),
)
if export.status == "completed":
    print(export.download_url)  # reused an existing export

create_for_study(study_id, export) async

Create an export of a study's report.

The export renders asynchronously: the returned record is normally queued, and :meth:get is the poll target for its status and download URL. Creation is idempotent — when an identical export is still live, that existing export is returned instead of a new one, and it comes back already completed with its download_url set. Check status before polling rather than polling unconditionally.

The artifact expires after a few days, at which point status becomes expired and the download URL is withheld.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required
export ReportExportCreate

Export request (format and optional section layout; project_params applies to project exports only)

required

Returns:

Type Description
ReportExportOut

The export record — queued, or an identical live export already

ReportExportOut

completed with its download URL

Raises:

Type Description
NotFoundError

If study doesn't exist

ValidationError

If the export request is invalid

AuthenticationError

If not authenticated

Example
from yousleep_common.models import ReportExportCreate

export = await client.report_exports.create_for_study(
    study_id=study.id,
    export=ReportExportCreate(format="md-bundle"),
)

get(export_id) async

Get one report export — the poll target for a created export.

Parameters:

Name Type Description Default
export_id UUID | str

UUID of the export

required

Returns:

Type Description
ReportExportOut

The export record; download_url is present only while the

ReportExportOut

export is completed and its artifact has not expired

Raises:

Type Description
NotFoundError

If the export doesn't exist or belongs to another user

AuthenticationError

If not authenticated

list(limit=20, offset=0, kind=None, format=None) async

List a page of the authenticated user's report exports, newest first.

Parameters:

Name Type Description Default
limit int

Maximum exports to return; the server clamps it to 1-100

20
offset int

How many exports to skip, for paging past the first page

0
kind ReportExportKind | None

Only study or only project exports; None for both

None
format ReportExportFormat | None

Only exports of this artifact format; None for every one

None

Returns:

Type Description
ReportExportListResponse

One page of exports, each with a fresh download URL while

ReportExportListResponse

completed and not expired, and total for everything matching

ReportExportListResponse

— narrowed by the filters, so a filtered history pages correctly

Raises:

Type Description
AuthenticationError

If not authenticated

Example
page = await client.report_exports.list(limit=50)
while page.offset + len(page.exports) < page.total:
    page = await client.report_exports.list(
        limit=50, offset=page.offset + len(page.exports)
    )

ReportsEndpoint

Bases: BaseEndpoint

Handles report generation API endpoints.

Provides methods for generating aggregated reports across recordings and analyses.

download_project_audit_csv(project_id, *, groups=None, statistics_window_start_ms=None, statistics_window_end_ms=None) async

Download the complete report audit table as CSV text.

The full counterpart of the report payload's capped audit appendix: one row per completed analysis, streamed from metadata (no statistics are computed). groups narrows to the report's by-group cohort; the window override must set both bounds or neither.

Returns:

Type Description
str

The CSV document (header + rows) as text.

get_unique_channels(project_id) async

Get unique channels across all recordings in all studies.

Returns a list of unique channel names with their locations across recordings.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required

Returns:

Type Description
list[UniqueChannel]

List of unique channels with their locations

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

Example
channels = await client.reports.get_unique_channels(
    project_id=project.id
)
for channel in channels:
    print(f"{channel.channel_name} ({channel.type})")
    print(f"  Found in {len(channel.records)} recordings")

StatusEndpoint

Bases: BaseEndpoint

Handles status-related API endpoints.

Provides health checks and system status information.

analyses(limit=None) async

Get recent analyses across all projects, newest first.

The server bounds the list by default so that polling it stays cheap during large batches. :meth:analyses_counts carries the full totals.

Parameters:

Name Type Description Default
limit int | None

Maximum analyses to return (1-500); the server's default when omitted

None

Returns:

Type Description
list[AnalysisOut]

List of recent analyses with parent project/study info

Raises:

Type Description
AuthenticationError

If not authenticated

analyses_counts() async

Get counts of recent analyses by status.

Returns:

Type Description
dict[str, int]

Dictionary mapping analysis status to count

Example
counts = await client.status.analyses_counts()
print(counts)  # {'pending': 5, 'running': 2, 'completed': 100}

Raises:

Type Description
AuthenticationError

If not authenticated

audit_actions() async

Get the audit action vocabulary: each action's name and readers.

Public. An audit view offers as filters the actions whose readers include its own, and reads each event's action by name.

Returns:

Type Description
list[AuditActionInfo]

Every action the server records, in the server's order

Example
org_actions = [
    a.action
    for a in await client.status.audit_actions()
    if "organisation" in a.readers
]

billing_rates() async

Get the public billing rates (platform pricing in credits).

Clients must display these rather than hardcoding prices, so the UI always matches what the server actually charges.

Returns:

Type Description
BillingRates

The current billing rates.

enabled_features() async

Get server features that are currently enabled.

Returns:

Type Description
ServerFeaturesEnabled

Server feature flags

Example
features = await client.status.enabled_features()
print(f"Login: {features.login}, Registration: {features.registration}")

Raises:

Type Description
AuthenticationError

If not authenticated

health() async

Check API health status.

Returns:

Type Description
dict[str, str]

Health status dictionary with 'status' key

Example
health = await client.status.health()
print(health)  # {'status': 'ok'}

limits() async

Get the server's client-relevant request limits.

Currently exposes the per-request item caps for the batch endpoints (deployment-configurable; e.g. the max number of files one batch analysis submission may target). Distinct from per-user usage quotas, which are available via client.user.usage_limits().

Returns:

Type Description
ServerLimits

The server's configured limits.

Example
limits = await client.status.limits()
print(limits.batch.max_analysis_submissions)

permissions() async

Get the permission vocabulary, with what an API key may be scoped to.

Public. The one list a permission editor renders and a key dialog offers from; key_scopeable is the server's decision.

Returns:

Type Description
list[PermissionInfo]

Every permission, in the server's order

Example
scopeable = [p.value for p in await client.status.permissions() if p.key_scopeable]

study_import_columns() async

Get the recognised column vocabulary for bulk study-data imports.

The server's authoritative copy of STUDY_IMPORT_COLUMNS (keys, aliases, units) — clients building a mapping UI fetch it from here instead of mirroring the constant, so new columns arrive without a package upgrade. Public, no authentication required.

Example
columns = await client.status.study_import_columns()
print([column.key for column in columns])

version() async

Get API server and common module version information.

Returns:

Type Description
ServerVersionInfo

ServerVersionInfo with api_version and common_version

Example
version = await client.status.version()
print(f"API: {version.api_version}, Common: {version.common_version}")

StudiesEndpoint

Bases: BaseEndpoint

Handles study-related API endpoints.

Studies represent individual subjects or participants within a project.

count(project_id, *, tags_any=None, name_contains=None, subject_key=None, missing_metadata_any=None) async

Count the studies a filter resolves to, without fetching them.

This is the expected_count a live-set selector write must present (the server re-resolves with the SAME predicate).

create(project_id, study) async

Create a new study in a project.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required
study StudyCreate

Study data (name is required; description, tags, metadata optional)

required

Returns:

Type Description
StudyOut

Created study information

Raises:

Type Description
ValidationError

If study data is invalid

NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

Example
from yousleep_common.models import StudyCreate

study = StudyCreate(
    name="Subject 001",
    description="First participant",
    age=35,
    sex="male",
)
created = await client.studies.create(project_id, study)

create_events(study_id, request) async

Create study-level events (e.g. lights off/on).

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required
request CreateStudyEventsRequest

Request containing list of study events to create

required

Returns:

Type Description
list[StudyEvent]

List of created study events

Raises:

Type Description
ValidationError

If event data is invalid

NotFoundError

If study doesn't exist

AuthenticationError

If not authenticated

delete(study_id) async

Delete a study.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study to delete

required

Returns:

Type Description
MessageResponse

Message confirming deletion

Raises:

Type Description
NotFoundError

If study doesn't exist

AuthenticationError

If not authenticated

delete_event(study_id, event_id) async

Delete a study-level event.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required
event_id UUID | str

UUID of the study event to delete

required

Returns:

Type Description
MessageResponse

Message confirming deletion

Raises:

Type Description
NotFoundError

If event doesn't exist

AuthenticationError

If not authenticated

get(study_id) async

Get a specific study by ID.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required

Returns:

Type Description
StudyOut

Study information

Raises:

Type Description
NotFoundError

If study doesn't exist

AuthenticationError

If not authenticated

get_report(study_id, include_biomarkers=False, soremp_threshold_min=15.0, statistics_window_start_ms=None, statistics_window_end_ms=None) async

Get a report for the study.

Returns event statistics for all completed analyses, optionally including biomarkers for staging analyses.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required
include_biomarkers bool

Whether to compute biomarkers for staging analyses

False
soremp_threshold_min float

Max minutes from sleep onset to the first REM epoch for a SOREMP

15.0
statistics_window_start_ms int | None

Optional window start override (ms)

None
statistics_window_end_ms int | None

Optional window end override (ms)

None

Returns:

Type Description
StudyReportResponse

StudyReportResponse with event stats, optional biomarkers, and window

Raises:

Type Description
NotFoundError

If study doesn't exist

AuthenticationError

If not authenticated

list(project_id, *, skip=None, limit=None, tags_any=None, name_contains=None, subject_key=None, missing_metadata_any=None, order_by=None) async

List the (optionally filtered) studies in a project.

tags_any/name_contains/subject_key are the selector filter vocabulary — this filtered listing is the preview of what a batch selector write addresses (see the Bulk Operations with Selectors guide).

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required
skip int | None

Pagination offset

None
limit int | None

Maximum studies to return (1-10,000). Without it the whole list is read, a project of up to 10,000 studies in one request

None
tags_any list[str] | None

Only studies carrying at least one of these tags

None
name_contains str | None

Only studies whose name contains this text

None
subject_key str | None

Only the studies of one subject — the key is compared case- and whitespace-insensitively

None
missing_metadata_any list[MetadataType] | None

Only studies that do not record at least one of these subject values

None
order_by StudyOrder | None

"created" (default) or "subject", which puts a person's studies together and in series order

None

Returns:

Type Description
list[StudyOut]

List of matching studies in the project

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

list_events(study_id) async

List all study-level events.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study

required

Returns:

Type Description
list[StudyEvent]

List of study events

Raises:

Type Description
NotFoundError

If study doesn't exist

AuthenticationError

If not authenticated

summary(project_id, *, tags_any=None, preview=None) async

Summarise a project's studies without listing them.

Every tag and every subject with its count, the studies carrying no tag, and the first studies of each set. One small answer at any project size, for pickers and breakdowns.

Parameters:

Name Type Description Default
project_id UUID | str

UUID of the project

required
tags_any list[str] | None

The tags a selection keeps; the summary then also counts the studies carrying none of them (excluded)

None
preview int | None

How many studies each set names (0-100, default 20)

None

Returns:

Type Description
StudySummary

The project's study summary

Raises:

Type Description
NotFoundError

If project doesn't exist

AuthenticationError

If not authenticated

update(study_id, study) async

Update an existing study (PATCH).

Universal field semantics:

  • Omit a field to leave it unchanged.
  • Include a field with a value to update it.
  • Include a field with None to clear it (where nullable in storage — description, tags, age, bmi, sex).

name cannot be cleared; sending name=None raises a validation error. Omit name to leave it unchanged.

Parameters:

Name Type Description Default
study_id UUID | str

UUID of the study to update

required
study StudyUpdate

Partial update (only included fields are modified)

required

Returns:

Type Description
StudyOut

Updated study information

Raises:

Type Description
NotFoundError

If study doesn't exist

ValidationError

If update data is invalid (e.g., name=None)

AuthenticationError

If not authenticated

Example
from yousleep_common.models import StudyUpdate

# Clear description and tags, leave everything else alone
study_update = StudyUpdate(description=None, tags=None)
updated = await client.studies.update(study_id, study_update)

UserEndpoint

Bases: BaseEndpoint

Handles user-related API endpoints.

Provides access to user information, history, and usage statistics.

change_password(request) async

Change the authenticated user's password.

Parameters:

Name Type Description Default
request ChangePasswordRequest

Current password (for verification) and the new password

required

Returns:

Type Description
MessageResponse

Message confirming the password change

Raises:

Type Description
AuthenticationError

If not authenticated

ValidationError

If the current password is wrong or the new one does not meet complexity requirements

Example
from yousleep_common.models import ChangePasswordRequest

await client.user.change_password(
    ChangePasswordRequest(
        current_password="old-password",
        new_password="new-password",
    )
)

create_api_key(request) async

Create an API key; the response carries the secret exactly once.

The account must have been enabled for keys by an administrator, and the request carries the account password again.

Parameters:

Name Type Description Default
request ApiKeyCreate

Name, scopes, optional description and lifetime, password

required

Returns:

Type Description
ApiKeyCreated

The key with its secret — store it now, it is not shown again

Raises:

Type Description
ForbiddenError

If the account may not create keys, or the client is itself authenticated with a key

ValidationError

If a scope is refused or the lifetime exceeds the server's maximum

Example
from yousleep_common.models import ApiKeyCreate
from yousleep_common.types import Permission

created = await client.user.create_api_key(
    ApiKeyCreate(
        name="Nightly pipeline",
        scopes=[Permission.RECORDING_CREATE, Permission.ANALYSIS_CREATE],
        expires_in_days=90,
        password="…",
    )
)
secret_manager.store("YOUSLEEP_API_KEY", created.secret)

delete() async

Delete the current user account.

This is a permanent action that deletes all user data.

Returns:

Type Description
MessageResponse

Message confirming deletion

Raises:

Type Description
AuthenticationError

If not authenticated

Example
response = await client.user.delete()
print(response.message)

export_audit(filters=None) async

Return your own audit trail as CSV text, oldest first.

get_current() async

Get information about the currently authenticated user.

Returns:

Type Description
UserOut

User information

Raises:

Type Description
AuthenticationError

If not authenticated

Example
user = await client.user.get_current()
print(f"Email: {user.email}")
print(f"Organization: {user.organization}")

get_organization() async

Get the caller's organization and their role in it.

Every project, credit balance and storage figure belongs to it.

Returns:

Type Description
UserOrganizationOut

The organization overview.

history(*, since_num_days=None, analysis_config_id=None, project_name=None, study_name=None, status=None, analysis_type=None, order=None, offset=None, limit=None) async

Get one page of the user's analysis history.

The history covers every analysis in every project the user can open, deleted ones included, ordered by the time each was queued. The server returns the newest first and a bounded page by default; total on the response counts every match, so page with offset until it is reached.

Parameters:

Name Type Description Default
since_num_days int | None

Only analyses from the last since_num_days days

None
analysis_config_id str | None

Only analyses of this configuration

None
project_name str | None

Only analyses in the project with this name

None
study_name str | None

Only analyses in studies with this name

None
status AnalysisStatus | None

Only analyses in this status

None
analysis_type AnalysisType | None

Only analyses of this type

None
order Literal['asc', 'desc'] | None

"desc" (the default, newest first) or "asc"

None
offset int | None

Analyses to skip

None
limit int | None

Maximum analyses to return; the server's default when omitted

None

Returns:

Type Description
HistoryResponse

Analysis history with list of analyses and total count

Raises:

Type Description
AuthenticationError

If not authenticated

Example
history = await client.user.history(since_num_days=7, limit=100)
print(f"Total analyses: {history.total}")
for analysis in history.history:
    print(f"- {analysis.id}: {analysis.status}")

list_api_keys() async

List the account's API keys — everything except the secret.

Expired keys stay listed until deleted, so the owner can see what needs rotating.

Returns:

Type Description
list[ApiKeyOut]

The keys, newest first

Example
for key in await client.user.list_api_keys():
    print(key.name, key.prefix, key.expires_at, key.expired)

list_audit(filters=None, *, offset=0, limit=50) async

Return your own audit trail, newest first.

Events about your account and membership, and what you did in projects. filters narrows by event types, a time window or an item.

Returns:

Type Description
AuditEventListResponse

One page of events (limit at most 200) and the total.

revoke_api_key(key_id) async

Revoke an API key; it stops authenticating on its next request.

Parameters:

Name Type Description Default
key_id UUID | str

The key's id, from the listing

required

Returns:

Type Description
MessageResponse

A confirmation message

Raises:

Type Description
NotFoundError

If the account holds no such key

update_profile(request) async

Update the authenticated user's own profile (name/position).

Omitted fields are left unchanged. Email/password changes go through their dedicated flows; usage_type changes are admin-mediated.

Parameters:

Name Type Description Default
request UserProfileUpdate

The profile fields to change

required

Returns:

Type Description
UserOut

The updated user

Example
from yousleep_common.models import UserProfileUpdate

user = await client.user.update_profile(
    UserProfileUpdate(name="Ada Lovelace", position="PI")
)

usage(analysis_config_id, since_num_days=30) async

Get the user's analysis usage statistics for a specific analysis config.

Parameters:

Name Type Description Default
analysis_config_id str

ID of the analysis configuration to get usage for

required
since_num_days int

Number of days to look back (default: 30)

30

Returns:

Type Description
AnalysisUsage

Usage statistics including analysis count, credit cost, and signal hours

Raises:

Type Description
AuthenticationError

If not authenticated

Example
usage = await client.user.usage("example-sleep-staging-analysis-v1")
print(f"Total analyses: {usage.analyses}")
print(f"Credit cost: {usage.credit_cost}")
print(f"Signal hours: {usage.signal_hours}")

usage_detailed() async

Get detailed resource usage statistics.

Returns:

Type Description
ResourceUsage

Resource usage with project, study, recording, and storage counts

Raises:

Type Description
AuthenticationError

If not authenticated

Example
usage = await client.user.usage_detailed()
print(f"Projects: {usage.projects}")

usage_limits() async

Get usage limits for the current user.

Returns:

Type Description
ResourceUsage

Resource usage limits

Raises:

Type Description
AuthenticationError

If not authenticated

Example
limits = await client.user.usage_limits()
print(f"Project limit: {limits.projects}")

WorkflowsEndpoint

Bases: BaseEndpoint

High-level, end-to-end analysis workflows built on the resource endpoints.

analyze_file(*, file, analysis_config_id, project_id=None, study_id=None, project_name=None, study_name=None, age=None, bmi=None, sex=None, project=None, study=None, parameters=None, channels=None, fetch=('events', 'biomarkers'), poll_interval=5.0, timeout=3600.0, on_progress=None, _ctx=None) async

Analyze a file end-to-end and return the results.

Creates a project and study (unless existing IDs are supplied), uploads the recording, submits an analysis with analysis_config_id, waits for it to complete, and fetches the requested results. Resources are kept — this method never deletes anything. Use :meth:analyze_file_temporary when you want them cleaned up.

Naming: when study_name is omitted it is inferred from the filename (night.edf -> study night). Metadata (age/bmi/sex) is optional and passed as plain scalars. Power users may pass full project/study models instead, mutually exclusively with the corresponding scalar arguments.

Parameters:

Name Type Description Default
file str | Path | BinaryIO

Path (or open binary file) of the recording to upload.

required
analysis_config_id str

ID of the analysis config to run.

required
project_id UUID | str | None

Analyze into this existing project instead of creating one.

None
study_id UUID | str | None

Analyze into this existing study (requires project_id).

None
project_name str | None

Name for the project to create (else auto-generated).

None
study_name str | None

Name for the study to create (else inferred from filename).

None
age int | None

Optional subject age (years) for a created study.

None
bmi float | None

Optional subject BMI for a created study.

None
sex str | None

Optional subject sex ("male"/"female"/"other"/"unknown").

None
project ProjectCreate | None

Full ProjectCreate model (instead of project_name).

None
study StudyCreate | None

Full StudyCreate model (instead of study_name + metadata).

None
parameters list[tuple[str, str | int | float | bool]] | None

Optional analysis parameters as (name, value) tuples.

None
channels list[str] | None

Optional explicit channel names for the analysis.

None
fetch tuple[FetchKind, ...]

Which results to return; defaults to events and biomarkers.

('events', 'biomarkers')
poll_interval float

Seconds between status checks while waiting.

5.0
timeout float | None

Max seconds to wait for the analysis before ClientTimeoutError.

3600.0
on_progress Callable[[int], None] | None

Optional upload-progress callback (percentage 0-100).

None
_ctx _RunContext | None

Internal run context (used by analyze_file_temporary); do not set.

None

Returns:

Type Description
AnalysisWorkflowResult

An AnalysisWorkflowResult with the recording, analysis, and results.

Raises:

Type Description
ValueError

If targeting arguments are combined incorrectly.

AnalysisWorkflowError

If the analysis ends in a non-completed state.

ClientTimeoutError

If the analysis does not finish within timeout.

YouSleepClientError

On any underlying API error.

analyze_file_temporary(*, file, analysis_config_id, project_id=None, study_id=None, project_name=None, study_name=None, age=None, bmi=None, sex=None, project=None, study=None, parameters=None, channels=None, fetch=('events', 'biomarkers'), poll_interval=5.0, timeout=3600.0, on_progress=None) async

Run :meth:analyze_file, deleting created resources on exit.

Takes the same arguments as :meth:analyze_file. On exit — whether the block completes normally or raises — this deletes whatever resources the workflow created, relying on server-side cascade (deleting the created project/study/recording removes its descendants and frees quota). Caller-supplied project_id/study_id are never deleted.

Example
async with client.workflows.analyze_file_temporary(
    file="night.edf", analysis_config_id=cfg_id
) as result:
    store(result.biomarkers)
# created project/study/recording/analysis are gone here

Yields:

Type Description
AsyncIterator[AnalysisWorkflowResult]

The AnalysisWorkflowResult, valid for the duration of the block.

analyze_files(*, files, analysis_config_id, project_id=None, project_name=None, project=None, study_id=None, parameters=None, channels=None, fetch=('events', 'biomarkers'), poll_interval=5.0, timeout=3600.0, max_concurrency=4, on_progress=None, _ctx=None) async

Analyze multiple files end-to-end and return per-file outcomes.

Creates one project (unless project_id is given) and one study per file (names inferred from filenames), uploads the recordings with bounded concurrency, submits all analyses in a single batch call, polls the whole batch with single status calls, and fetches results for completed analyses. Resources are kept — use :meth:analyze_files_temporary for automatic cleanup.

Per-file failures never raise: each input file yields a :class:FileAnalysisOutcome (completed/failed/timeout) and the rest of the batch continues. Only argument errors and whole-batch failures (e.g. the project cannot be created) raise.

One study per file. study_id targets an existing study and is therefore only valid with a single file — a study holds exactly one recording. Passing it with several files raises ValueError.

Multiple nights of one subject are several studies, one per night, linked by a shared subject_key.

Per-file subject metadata is not supported here — create studies via the lower-level endpoints if needed.

Parameters:

Name Type Description Default
files Sequence[str | Path | BinaryIO]

Paths (or open binary files) of the recordings, 1-500.

required
analysis_config_id str

ID of the analysis config to run on every file.

required
project_id UUID | str | None

Analyze into this existing project instead of creating one.

None
project_name str | None

Name for the project to create (else auto-generated).

None
project ProjectCreate | None

Full ProjectCreate model (instead of project_name).

None
study_id UUID | str | None

Existing study to upload into (requires project_id). Valid with a single file only — a study holds one recording.

None
parameters list[tuple[str, str | int | float | bool]] | None

Optional analysis parameters as (name, value) tuples.

None
channels list[str] | None

Optional explicit channel names for the analyses.

None
fetch tuple[FetchKind, ...]

Which results to return per file; defaults to events + biomarkers.

('events', 'biomarkers')
poll_interval float

Seconds between batch status polls while waiting.

5.0
timeout float | None

Max seconds to wait for the whole batch; analyses still running afterwards yield timeout outcomes (no raise).

3600.0
max_concurrency int

Max parallel uploads / per-item result fetches.

4
on_progress Callable[[str | Path | BinaryIO, int], None] | None

Optional upload-progress callback receiving (file, percent) — the input file object/path and its upload progress (0-100). Calls from concurrent uploads interleave.

None
_ctx _BatchRunContext | None

Internal run context (used by analyze_files_temporary); do not set.

None

Returns:

Type Description
BatchAnalysisWorkflowResult

A BatchAnalysisWorkflowResult with one outcome per input file, in

BatchAnalysisWorkflowResult

input order.

Raises:

Type Description
ValueError

If arguments are combined incorrectly or out of range.

YouSleepClientError

On whole-batch API failures (before any per-file work exists).

analyze_files_temporary(*, files, analysis_config_id, project_id=None, project_name=None, project=None, study_id=None, parameters=None, channels=None, fetch=('events', 'biomarkers'), poll_interval=5.0, timeout=3600.0, max_concurrency=4, on_progress=None) async

Run :meth:analyze_files, deleting created resources on exit.

Takes the same arguments as :meth:analyze_files. On exit — whether the block completes normally or raises — this deletes whatever the workflow created: the created project (one cascade delete), or each created study when the caller supplied the project, or each uploaded recording when the caller supplied a shared study. Caller-supplied resources are never deleted.

Yields:

Type Description
AsyncIterator[BatchAnalysisWorkflowResult]

The BatchAnalysisWorkflowResult, valid for the duration of the block.