{
  "slug": "sdk/python",
  "title": "Python SDK",
  "description": "Send Cuitty events from Python — sync or async, with built-in batching and HMAC signing.",
  "url": "https://cuitty.com/docs/sdk/python",
  "markdown_url": "https://cuitty.com/docs/sdk/python.md",
  "json_url": "https://cuitty.com/docs/sdk/python.json",
  "frontmatter": {
    "title": "Python SDK",
    "description": "Send Cuitty events from Python — sync or async, with built-in batching and HMAC signing.",
    "order": 2,
    "section": "SDK",
    "updatedAt": "2026-04-27"
  },
  "headings": [
    {
      "depth": 1,
      "slug": "python-sdk",
      "text": "Python SDK"
    },
    {
      "depth": 2,
      "slug": "planned-api",
      "text": "Planned API"
    },
    {
      "depth": 2,
      "slug": "wire-protocol-equivalent-today",
      "text": "Wire-protocol equivalent today"
    },
    {
      "depth": 2,
      "slug": "live-performance-numbers",
      "text": "Live performance numbers"
    },
    {
      "depth": 2,
      "slug": "see-also",
      "text": "See also"
    }
  ],
  "body_markdown": "# Python SDK\n\n> **Status: Preview.** The Python SDK is on the Phase 3 roadmap. Until it ships you can use [the wire protocol directly](/docs/reference/wire-protocol) — every Python HTTP client supports the small surface area required.\n\n## Planned API\n\n```python\nfrom cuitty import Client\n\nc = Client(\n    portal_url=\"https://app.cuitty.com\",\n    project_id=PROJECT_ID,\n    api_key=API_KEY,\n)\n\nc.audit(\n    actor=\"alice@example.com\",\n    action=\"secret.rotate\",\n    resource=\"stripe.live_key\",\n)\n\nc.flush()\n```\n\n## Wire-protocol equivalent today\n\n```python\nimport hmac, hashlib, json, os, time\nimport urllib.request\n\nbody = json.dumps({\n    \"events\": [{\n        \"type\": \"audit\",\n        \"ts\": time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()),\n        \"data\": {\"actor\": \"alice@example.com\",\n                 \"action\": \"secret.rotate\",\n                 \"resource\": \"stripe.live_key\"},\n    }],\n}).encode()\n\nsig = hmac.new(os.environ[\"CUITTY_WEBHOOK_SECRET\"].encode(),\n               body, hashlib.sha256).hexdigest()\n\nreq = urllib.request.Request(\n    \"https://app.cuitty.com/api/ingest\",\n    data=body,\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['CUITTY_API_KEY']}\",\n        \"Content-Type\": \"application/json\",\n        \"X-Cuitty-Signature\": sig,\n    },\n)\nprint(urllib.request.urlopen(req).read())\n```\n\n## Live performance numbers\n\nSee live benchmarks at [https://benchmarks.cuitty.com/sdks/python](https://benchmarks.cuitty.com/sdks/python).\n\n## See also\n\n- [Wire protocol](/docs/reference/wire-protocol)\n- [SDK parity & divergence](/docs/sdk/parity)",
  "body_html": "<h1 id=\"python-sdk\">Python SDK</h1>\n<blockquote>\n<p><strong>Status: Preview.</strong> The Python SDK is on the Phase 3 roadmap. Until it ships you can use <a href=\"/docs/reference/wire-protocol\">the wire protocol directly</a> — every Python HTTP client supports the small surface area required.</p>\n</blockquote>\n<h2 id=\"planned-api\">Planned API</h2>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#F97583\">from</span><span style=\"color:#E1E4E8\"> cuitty </span><span style=\"color:#F97583\">import</span><span style=\"color:#E1E4E8\"> Client</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">c </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> Client(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    portal_url</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"https://app.cuitty.com\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    project_id</span><span style=\"color:#F97583\">=</span><span style=\"color:#79B8FF\">PROJECT_ID</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    api_key</span><span style=\"color:#F97583\">=</span><span style=\"color:#79B8FF\">API_KEY</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">c.audit(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    actor</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"alice@example.com\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    action</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"secret.rotate\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    resource</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"stripe.live_key\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">c.flush()</span></span></code></pre>\n<h2 id=\"wire-protocol-equivalent-today\">Wire-protocol equivalent today</h2>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#F97583\">import</span><span style=\"color:#E1E4E8\"> hmac, hashlib, json, os, time</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">import</span><span style=\"color:#E1E4E8\"> urllib.request</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">body </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> json.dumps({</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">    \"events\"</span><span style=\"color:#E1E4E8\">: [{</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">        \"type\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"audit\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">        \"ts\"</span><span style=\"color:#E1E4E8\">: time.strftime(</span><span style=\"color:#9ECBFF\">\"%Y-%m-</span><span style=\"color:#79B8FF\">%d</span><span style=\"color:#9ECBFF\">T%H:%M:%SZ\"</span><span style=\"color:#E1E4E8\">, time.gmtime()),</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">        \"data\"</span><span style=\"color:#E1E4E8\">: {</span><span style=\"color:#9ECBFF\">\"actor\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"alice@example.com\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">                 \"action\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"secret.rotate\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">                 \"resource\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"stripe.live_key\"</span><span style=\"color:#E1E4E8\">},</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    }],</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">}).encode()</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">sig </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> hmac.new(os.environ[</span><span style=\"color:#9ECBFF\">\"CUITTY_WEBHOOK_SECRET\"</span><span style=\"color:#E1E4E8\">].encode(),</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">               body, hashlib.sha256).hexdigest()</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">req </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> urllib.request.Request(</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">    \"https://app.cuitty.com/api/ingest\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    data</span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\">body,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    headers</span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\">{</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">        \"Authorization\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#F97583\">f</span><span style=\"color:#9ECBFF\">\"Bearer </span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">os.environ[</span><span style=\"color:#9ECBFF\">'CUITTY_API_KEY'</span><span style=\"color:#E1E4E8\">]</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">        \"Content-Type\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"application/json\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#9ECBFF\">        \"X-Cuitty-Signature\"</span><span style=\"color:#E1E4E8\">: sig,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    },</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"><span style=\"color:#79B8FF\">print</span><span style=\"color:#E1E4E8\">(urllib.request.urlopen(req).read())</span></span></code></pre>\n<h2 id=\"live-performance-numbers\">Live performance numbers</h2>\n<p>See live benchmarks at <a href=\"https://benchmarks.cuitty.com/sdks/python\">https://benchmarks.cuitty.com/sdks/python</a>.</p>\n<h2 id=\"see-also\">See also</h2>\n<ul>\n<li><a href=\"/docs/reference/wire-protocol\">Wire protocol</a></li>\n<li><a href=\"/docs/sdk/parity\">SDK parity &#x26; divergence</a></li>\n</ul>",
  "links_out": [
    "/docs/reference/wire-protocol",
    "https://benchmarks.cuitty.com/sdks/python",
    "/docs/sdk/parity"
  ]
}