Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

permissio

Official Python SDK for the Permissio Partner API. Build document-signing workflows directly into your product.

Installation

pip install permissio

Requires Python 3.9+ and httpx.

Quick start

from permissio import Permissio

client = Permissio(api_key="sk_live_…")  # or sk_test_… for sandbox

# 1. Upload a template PDF
result = client.templates.get_upload_url()
import httpx
httpx.put(
    result["upload_url"],
    content=open("nda.pdf", "rb").read(),
    headers={"Content-Type": "application/pdf"},
)

# 2. Create a template
template = client.templates.create(
    name="NDA — Mutual Confidentiality Agreement",
    document_url=result["object_path"],
    recipients=[
        {"role_name": "Disclosing Party", "signing_order": 1},
        {"role_name": "Receiving Party",  "signing_order": 2},
    ],
    variables=[
        {"name": "effective_date", "type": "date",   "required": True},
        {"name": "party_a_name",   "type": "string", "required": True},
    ],
)

# 3. Create an envelope (draft)
envelope = client.envelopes.create(
    template_id=template["id"],
    title="NDA — Acme Corp",
    signers=[
        {"role_name": "Disclosing Party", "email": "alice@example.com", "name": "Alice Nguyen"},
        {"role_name": "Receiving Party",  "email": "bob@example.com",   "name": "Bob Smith"},
    ],
    variables={
        "effective_date": "2026-05-04",
        "party_a_name": "Smartshares Limited",
    },
    expires_in_days=14,
)

# 4. Send it — dispatches signer invite emails
client.envelopes.send(envelope["id"])

# 5. Check status
detail = client.envelopes.get(envelope["id"])
print(detail["status"])  # "sent", "in_progress", "completed", …

# 6. Download the executed PDF when complete
if detail["status"] == "completed":
    response = client.envelopes.get_signed_document(envelope["id"])
    with open("signed.pdf", "wb") as f:
        f.write(response.content)

Async client

import asyncio
from permissio import AsyncPermissio

async def main():
    async with AsyncPermissio(api_key="sk_live_…") as client:
        envelope = await client.envelopes.create(
            template_id="tpl_…",
            signers=[{"role_name": "Signer", "email": "a@b.com", "name": "Alice"}],
        )
        await client.envelopes.send(envelope["id"])

asyncio.run(main())

Client options

client = Permissio(
    api_key="sk_live_…",                        # required
    base_url="https://app.permissio.us/api",    # optional — default shown
    timeout=30.0,                               # optional — seconds, default 30
    http_client=httpx.Client(...),              # optional — custom httpx client
)

Error handling

All API errors are raised as PermissioApiError:

from permissio import Permissio, PermissioApiError

try:
    client.envelopes.send("env_…")
except PermissioApiError as err:
    print(err.code)       # e.g. "invalid_state"
    print(err.status)     # HTTP status, e.g. 409
    print(err.request_id) # include in support tickets
    print(err.details)    # additional structured info

Common error codes

Code Status Meaning
unauthorized 401 Invalid or missing API key
forbidden 403 Key lacks the required scope
not_found 404 Resource does not exist or belongs to another tenant
invalid_state 409 Operation not allowed in the envelope's current state
idempotency_conflict 409 Same idempotency key, different request body
idempotency_in_progress 425 Concurrent request with the same key is still in flight
validation_error 422 Request body failed validation

Idempotency

Write operations auto-generate a UUID as the Idempotency-Key header. Override to replay a specific request:

import uuid
key = str(uuid.uuid4())
envelope = client.envelopes.create(template_id="tpl_…", signers=[], idempotency_key=key)
# Safe to retry with the same key — server returns the same envelope:
envelope2 = client.envelopes.create(template_id="tpl_…", signers=[], idempotency_key=key)
assert envelope["id"] == envelope2["id"]

Webhooks

from flask import Flask, request, abort
from permissio.namespaces import WebhooksNamespace

app = Flask(__name__)

@app.post("/webhooks/permissio")
def handle_webhook():
    if not WebhooksNamespace.verify_signature(
        request.data,
        request.headers.get("Permissio-Signature", ""),
        ENDPOINT_SECRET,
    ):
        abort(400)
    event = request.json
    if event["type"] == "envelope.completed":
        print(f"Envelope {event['data']['envelope']['id']} completed!")
    return "", 200

Namespaces

Namespace Methods
client.envelopes list(), create(), get(), send(), void(), get_signed_document(), get_certificate()
client.templates list(), get(), create(), get_upload_url()
client.webhooks list(), create(), get(), verify_signature()

Resources

Download files

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

Source Distribution

permissio-0.1.0b1.tar.gz (120.9 kB view details)

Uploaded Source

Built Distribution

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

permissio-0.1.0b1-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file permissio-0.1.0b1.tar.gz.

File metadata

  • Download URL: permissio-0.1.0b1.tar.gz
  • Upload date:
  • Size: 120.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for permissio-0.1.0b1.tar.gz
Algorithm Hash digest
SHA256 43783e0be17d139de9b0b90d22e1b14db4ccb24b0b5729b0edceb6f9a27317e8
MD5 ced458aadb041c9c26fe951c4b97d0de
BLAKE2b-256 e5acca8cc485e4c1bbdf6bbfdb2ba54c6503b0e7f5cefd9943c60d44f72eb4b1

See more details on using hashes here.

File details

Details for the file permissio-0.1.0b1-py3-none-any.whl.

File metadata

  • Download URL: permissio-0.1.0b1-py3-none-any.whl
  • Upload date:
  • Size: 15.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for permissio-0.1.0b1-py3-none-any.whl
Algorithm Hash digest
SHA256 2f223a43b5330294d9aeca07e37fc1ae7206a901f0214c5537600d2355ffac4c
MD5 bf95ddccfb89f36ca30da0099c5ff4a0
BLAKE2b-256 311e2fffb5b2c58f38ccdb74be2c32a3266a9c7a93cd593420f549747c428087

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0b1 This release

2 files

Supported by

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