Skip to main content

Jobber Python Client

Minimal Python client for Jobber GraphQL API with OAuth 2.0 support.

Features

  • One-time authentication: Browser-based OAuth flow stores tokens in Doppler
  • Visual confirmation URLs: Get web links to verify API operations in Jobber UI (Quick Win Guide)
  • Fail-fast errors: All failures raise exceptions with context
  • Token auto-refresh: Transparent token refresh before expiration
  • Rate limit awareness: Exposes throttle status, raises before exceeding limits
  • Minimal dependencies: Only requests and oauthlib required

Installation

# Install from PyPI
pip install jobber-python-client

# Or clone repository for development
git clone https://github.com/tainora/jobber-python-client.git
cd jobber-python-client
uv sync

Prerequisites

  1. Jobber Developer Account: Create app at https://developer.getjobber.com/
  2. Doppler CLI: Install from https://docs.doppler.com/docs/install-cli
  3. OAuth Credentials: Store in Doppler:
    doppler secrets set JOBBER_CLIENT_ID="your_client_id" \
      JOBBER_CLIENT_SECRET="your_client_secret" \
      --project claude-config --config dev
    

Quick Start

Step 1: Authenticate (one-time)

uv run jobber_auth.py

This will:

  1. Open browser for Jobber authorization
  2. Exchange authorization code for tokens
  3. Store tokens in Doppler

Step 2: Use in AI agent code

from jobber import JobberClient

# Create client (loads credentials from Doppler)
client = JobberClient.from_doppler()

# Execute GraphQL queries
result = client.execute_query("""
    query {
        clients(first: 10) {
            nodes {
                id
                firstName
                lastName
            }
            totalCount
        }
    }
""")

print(f"Total clients: {result['clients']['totalCount']}")

Visual Confirmation URLs (Quick Win!)

Problem: API operations feel abstract. You create a client via API, but can you see it in Jobber's web interface?

Solution: YES! Include jobberWebUri in your queries to get clickable web links.

from jobber import JobberClient

client = JobberClient.from_doppler()

# IMPORTANT: Include jobberWebUri in mutation response
mutation = """
    mutation CreateClient($input: ClientCreate!) {
        clientCreate(input: $input) {
            client {
                id
                firstName
                lastName
                jobberWebUri  # ← Returns web URL!
            }
        }
    }
"""

result = client.execute_query(mutation, {
    'input': {
        'firstName': 'John',
        'lastName': 'Doe'
    }
})

created = result['clientCreate']['client']

# Show clickable link for visual confirmation
print(f"✅ Client created: {created['firstName']} {created['lastName']}")
print(f"🔗 View in Jobber: {created['jobberWebUri']}")
# Click link → See client in Jobber web UI → Instant verification!

Available URL Fields

Field Available On Purpose
jobberWebUri Most resources (Client, Job, Quote, Invoice) Direct link to resource in Jobber web UI
previewUrl Quotes Client Hub link for customer approval

Quick Start

Run the complete example:

uv run examples/visual_confirmation_urls.py

Learn more: Visual Confirmation URLs Guide - Comprehensive patterns, best practices, and use cases.

Pro tip: ALWAYS include jobberWebUri in mutations for instant visual verification!

API Reference

JobberClient

Main client for executing GraphQL queries.

JobberClient.from_doppler(project, config)

Create client loading credentials from Doppler.

Parameters:

  • project (str): Doppler project name (default: "claude-config")
  • config (str): Doppler config name (default: "dev")

Returns: JobberClient instance

Raises:

  • ConfigurationError: Doppler secrets not found
  • AuthenticationError: Token loading fails

Example:

client = JobberClient.from_doppler("claude-config", "dev")

client.execute_query(query, variables=None, operation_name=None)

Execute GraphQL query.

Parameters:

  • query (str): GraphQL query string
  • variables (dict, optional): Query variables
  • operation_name (str, optional): Operation name for multi-operation queries

Returns: dict - Response data (response['data'])

Raises:

  • AuthenticationError: Token invalid or expired
  • RateLimitError: Rate limit threshold exceeded (< 20% points available)
  • GraphQLError: Query execution failed
  • NetworkError: HTTP request failed

Example:

result = client.execute_query(
    query="""
        query GetClients($first: Int!) {
            clients(first: $first) {
                nodes { id firstName }
            }
        }
    """,
    variables={'first': 50}
)

client.get_throttle_status()

Get last known rate limit status.

Returns: dict or None

  • currentlyAvailable: Points available now
  • maximumAvailable: Total capacity (typically 10,000)
  • restoreRate: Points restored per second (typically 500)

Example:

status = client.get_throttle_status()
if status:
    print(f"{status['currentlyAvailable']} points available")

Error Handling

All methods raise exceptions on failure. Handle appropriately:

from jobber import (
    JobberClient,
    AuthenticationError,
    RateLimitError,
    GraphQLError,
    NetworkError,
)

try:
    client = JobberClient.from_doppler()
    result = client.execute_query("{ clients { totalCount } }")

except AuthenticationError as e:
    # Resolution: Run jobber_auth.py
    print(f"Auth error: {e}")

except RateLimitError as e:
    # Resolution: Wait for points to restore
    wait_seconds = e.context.get('wait_seconds', 0)
    print(f"Rate limited. Wait {wait_seconds:.1f}s")

except GraphQLError as e:
    # Resolution: Check query syntax
    print(f"Query error: {e.errors}")

except NetworkError as e:
    # Resolution: Check connectivity
    print(f"Network error: {e}")

Examples

See examples/ directory:

Run examples:

uv run --with . examples/basic_usage.py

Architecture

See ADR-0001: Jobber API Client Architecture for design decisions.

Key Principles

  1. Fail-fast: Raise exceptions immediately, no retry or fallback
  2. Caller control: AI agent decides error recovery strategy
  3. Minimal abstraction: Thin wrapper over GraphQL HTTP requests
  4. Explicit configuration: No default values or silent assumptions

Module Structure

jobber/
├── __init__.py       # Public API exports
├── client.py         # JobberClient class
├── auth.py           # TokenManager (Doppler integration)
├── graphql.py        # GraphQLExecutor (HTTP requests)
└── exceptions.py     # Exception hierarchy

Rate Limiting

Jobber API limits:

  • 10,000 points available
  • 500 points/second restore rate
  • Query costs vary (9-50 points typical)

This library:

  • Raises RateLimitError when available points < 20% of maximum
  • Exposes throttle_status in exception context
  • Caller decides when to wait or abort

Token Management

Tokens expire after 60 minutes. This library:

  • Proactively refreshes 5 minutes before expiration (background thread)
  • Reactively refreshes on 401 errors (retry once)
  • Updates Doppler with new tokens automatically

Token storage in Doppler:

JOBBER_ACCESS_TOKEN=eyJhbGc...
JOBBER_REFRESH_TOKEN=def502...
JOBBER_TOKEN_EXPIRES_AT=1731873600

SLOs

  • Availability: Simple code paths, minimal failure modes
  • Correctness: Type-safe responses, validation at boundaries
  • Observability: Exceptions include full context and stack traces
  • Maintainability: < 500 LOC core library, clear contracts

Development

Running Tests

pytest -v

Type Checking

mypy jobber/

Linting

ruff check jobber/

Contributing

This project uses Conventional Commits and semantic-release.

Commit format:

feat: add custom field support
fix: handle token refresh race condition
docs: update error handling examples

License

MIT

Support

Changelog

See CHANGELOG.md (auto-generated by semantic-release).

Metadata

Release files for jobber-python-client 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jobber-python-client 0.2.1
File Size Uploaded
jobber_python_client-0.2.1.tar.gz 268.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jobber-python-client 0.2.1
File Interpreter ABI Platform
jobber_python_client-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 289.8 kB

Release files / jobber_python_client-0.2.1.tar.gz

Download URL jobber_python_client-0.2.1.tar.gz
Size 268.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f9af5a344779040480013bc95b76ec52f8848e657673c9559f6c84452b5df162
BLAKE2b-256 checksum
How to use checksums
f2d9f49c6ba647ef279191cfbafd9f097c365284195e5bc9ca8aaeb4d51413cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.11 {"installer":{"name":"uv","version":"0.9.11"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / jobber_python_client-0.2.1-py3-none-any.whl

Download URL jobber_python_client-0.2.1-py3-none-any.whl
Size 21.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
442ca5fb4d17cbb6945f7fdfcca81787ec53f79957dd1829d2157990b522c41c
BLAKE2b-256 checksum
How to use checksums
a254e19ad748e3136b1069e5f91b96aa10e6d79b0f5927b83dd462ad767140b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.11 {"installer":{"name":"uv","version":"0.9.11"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page