Skip to main content

Official Python SDK for the Velocity OS Extension API

Project description

velocity-os

Official Python SDK for the Velocity OS Extension API.

pip install velocity-os

Build third-party plugins (CRM syncs, accountability apps, industry-specific tools, etc.) without writing HTTP boilerplate. The SDK gives you typed methods, automatic retry, scope pre-checks, and a decorator-based webhook router.

from velocity_os import VelocityOS

vos = VelocityOS(
    api_key=os.environ["VOS_EXT_KEY"],
    webhook_secret=os.environ["VOS_WEBHOOK_SECRET"],
)

me = vos.whoami()
print(me.user.email, me.extension.granted_scopes)

tasks = vos.tasks.list()
for t in tasks.open:
    print(t.action, t.deadline)

vos.tasks.create(
    action="Schedule weekly review",
    module_source="L12",
    quadrant="schedule",
)

Install

pip install velocity-os

Requires Python 3.9+.

For local development inside the Velocity OS monorepo:

pip install -e sdk/python

Authentication

You need two things from your Velocity OS developer dashboard:

  1. An install API key (vos_ext_…) — generated when a Velocity OS user installs your extension. One key per (user, extension) pair.
  2. A webhook signing secret (whsec_…) — generated when you register your extension. Used to verify inbound webhooks. Optional if your extension only reads data.
vos = VelocityOS(
    api_key="vos_ext_...",
    webhook_secret="whsec_...",  # optional, only needed for webhooks
)

Available scopes

Each extension declares the scopes it needs at registration. Users grant a subset (or all) of those scopes when they install. Calls to endpoints the extension wasn't granted access to raise ScopeMissingError before the network call (after the first whoami() populates the scope cache).

Scope What it grants
read:profile Founder profile fields
read:modules Outputs of completed modules
read:offers Approved/active offers
read:team Team roster
read:partnerships Partnerships list
read:tasks Action backlog (open + completed)
read:academy Academy programs (academy tier only)
write:tasks Append/update tasks
write:entities Add team / partnerships
write:notes Append notes

Resource reference

All resources are accessed as attributes of the VelocityOS client. Each method docstring states which scope is required.

vos.whoami()WhoAmI

Returns the user this key acts on behalf of and the extension metadata. Cached for the lifetime of the client; pass refresh=True to re-fetch.

me = vos.whoami()
me.user.email           # 'user@example.com'
me.user.tier            # 'growth'
me.extension.name       # 'GHL Sync'
me.extension.granted_scopes  # ['read:tasks', 'write:tasks', ...]
me.sandbox              # True if dev sandbox mode is on

vos.profile.get()dict

Returns the user's whitelisted founder profile fields. Scope: read:profile

vos.modules.list()list[ModuleSummary]

Returns a summary of every module the user has touched. Scope: read:modules

for m in vos.modules.list():
    print(m.code, m.status, m.completed_at)

vos.modules.get(code)Module

Returns the parsed JSON output for one module (e.g. "F6", "C3"). Raises NotFoundError if the user has not completed it. Scope: read:modules

f6 = vos.modules.get("F6")
print(f6.output["pricing_model"])

vos.offers.list()list[Offer]

Returns approved/active offers from the user's entity registry. Scope: read:offers

vos.team.list()list[TeamMember]

Returns the user's team roster. Scope: read:team

vos.team.add(name, role="", email="")TeamMember

Appends a new team member. Scope: write:entities

vos.partnerships.list()list[Partnership]

Returns the user's partnerships. Scope: read:partnerships

vos.tasks.list()TaskList

Returns combined open and completed actions. Scope: read:tasks

tasks = vos.tasks.list()
for t in tasks.open:
    print(t.action, t.quadrant, t.deadline)
for c in tasks.completed:
    print(c.action, c.completed_at)

vos.tasks.create(action, module_source, **kwargs)Task

Appends a new action to the user's backlog. Triggers a tasks.created webhook to subscribed extensions. Scope: write:tasks

vos.tasks.create(
    action="Schedule weekly review with COO",
    module_source="L12",
    category="people",
    timeframe="this_week",
    quadrant="schedule",
    deadline="2026-04-20",
    assignee="Alex",
)

vos.academy.programs()list[AcademyProgram]

Returns academy programs owned by the user. Scope: read:academy (populated only for academy tier).


Webhooks

If you registered a webhook_url in your developer dashboard, Velocity OS will POST signed events to that URL when relevant things happen in any installed user's account. The SDK ships with a decorator-based router and automatic HMAC-SHA256 signature verification.

handler = vos.webhook_handler()

@handler.on("tasks.created")
def on_task(payload):
    print("new task:", payload["action"])

@handler.on("tasks.updated")
def on_task_changed(payload):
    print("updated:", payload["action"], "->", payload["quadrant"])

@handler.on("module.updated")
def on_module(payload):
    invalidate_cache(payload["module_code"])

@handler.on("subscription.changed")
def on_sub(payload):
    print("user is now on tier:", payload["new_tier"])

@handler.on("*")  # catch-all, runs in addition to specific handlers
def on_any(envelope):
    log_event(envelope["event_type"], envelope["delivery_id"])

Wiring it up

The handler is framework-agnostic — it takes a headers mapping and the raw request body bytes. Here's Flask:

from flask import request

@app.post("/webhook")
def webhook():
    result, status = handler.handle(request.headers, request.get_data())
    return result, status

FastAPI:

from fastapi import Request

@app.post("/webhook")
async def webhook(request: Request):
    body = await request.body()
    result, status = handler.handle(request.headers, body)
    return JSONResponse(result, status_code=status)

The SDK verifies the X-VOS-Signature header BEFORE calling any of your handlers. Requests with bad, missing, or expired signatures (>5 minutes old by default) return (401, ...) and never invoke your code.

Event types

Event Required scope on the install Payload shape
module.updated read:modules {module_code, completed_at}
tasks.created read:tasks {module_source, action, category, timeframe}
tasks.updated read:tasks {module_source, action, quadrant, assignee, deadline}
team.added read:team {name, role, email}
subscription.changed read:profile {new_tier, source}
extension.installed read:profile {extension_slug, scopes}
extension.revoked read:profile {install_id}

Manual signature verification

If you can't use the SDK's handler (e.g. you're routing through a different server), the standalone helper is exported:

from velocity_os import verify_signature

if not verify_signature(secret, signature_header, raw_body):
    abort(401)

Errors

All SDK errors inherit from VelocityOSError. Catch the specific subclass you care about:

from velocity_os import (
    AuthenticationError,    # 401, key invalid/revoked
    ScopeMissingError,      # 403, missing required scope
    NotFoundError,          # 404, resource doesn't exist
    RateLimitError,         # 429, rate limited
    ServerError,            # 5xx, after retries exhausted
    APIError,               # base for any other API error
)

try:
    vos.tasks.create(action="...", module_source="L12")
except ScopeMissingError as exc:
    print("missing scope:", exc.scope)
except AuthenticationError:
    print("user revoked the key, ask them to reinstall")

The SDK retries network errors and 5xx responses with exponential backoff (0.5s, 1.5s, 4s) before giving up. Auth/scope errors fail immediately so you don't waste retries on a revoked key.


Local pre-flight scope checks

After the first vos.whoami() call, the SDK caches your install's granted scopes. Subsequent resource calls check the cache locally before making the network call:

vos = VelocityOS(api_key="vos_ext_...", ...)
vos.whoami()  # populates scope cache

vos.tasks.create(action="...", module_source="L12")
# If write:tasks isn't in your granted scopes, raises ScopeMissingError
# WITHOUT making a network call.

If you skip whoami() entirely, the SDK falls back to letting the server return 403 scope_required, which the SDK still translates into the same ScopeMissingError exception — same UX, just one extra round-trip.


Sandbox mode

If you're a developer testing your own extension on yourself, toggle "sandbox mode" on in your developer dashboard. Calls to the API using your own keys will then return deterministic dummy data instead of your real account state. The me.sandbox field on whoami() and the Response.sandbox flag will be True so you can detect it.

This only affects YOUR keys. Other users who installed your extension hit their real data.


User-Agent

The SDK sets a User-Agent: VelocityOS-Python-SDK/0.1.0 header on every request so Velocity OS server logs can identify SDK traffic. Override with user_agent= if you want to brand your extension:

vos = VelocityOS(
    api_key="vos_ext_...",
    user_agent="ghl-sync/2.4.0",
)

Version

from velocity_os import __version__
print(__version__)  # '0.1.0'

See also

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

velocity_os-0.1.0.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

velocity_os-0.1.0-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file velocity_os-0.1.0.tar.gz.

File metadata

  • Download URL: velocity_os-0.1.0.tar.gz
  • Upload date:
  • Size: 21.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for velocity_os-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ed1e24932ac2fcb4d71d2a8800eb0957500fefd0e7b7c40e11361c73893b89d6
MD5 c25a362ac00c1bcf60db7691a5ebc920
BLAKE2b-256 380ad4cc7e595f7545b40d79fc3dca75024acfe7f52145c7a1d156020507d7fc

See more details on using hashes here.

File details

Details for the file velocity_os-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: velocity_os-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for velocity_os-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7a4d97f0d7396ab2a17cd18a417bcbb7cf85c0ba22a3f0fe52c3f3d5afe77212
MD5 3b5b7ddb65a18aa3abd499b32589bc5d
BLAKE2b-256 7a3993bcd09c3c1eb10cec93a178748da105eff9d5954f60f46e9c4ad10d46bb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page