Skip to main content

Python client for the Fizzy API

Project description

Fizzy API Client

Python client for the Fizzy API.

Tests PyPI version Python 3.10+ License: MIT

Installation

pip install fizzy-api-client

Quick Start

from fizzy import FizzyClient

client = FizzyClient(
    token="your-api-token",
    account_slug="your-account-slug"
)

# List boards
boards = client.boards.list()

# Create a card
card = client.cards.create(
    board_id="board-123",
    title="My new card"
)

# List cards with filtering
cards = client.cards.list(
    board_id="board-123",
    tag_ids=["tag-1", "tag-2"],
    status="open"
)

Authentication

Personal Access Token

The recommended authentication method for scripts and integrations:

from fizzy import FizzyClient

client = FizzyClient(
    token="your-personal-access-token",
    account_slug="your-account-slug"
)

Magic Link Session

For native applications that need user authentication:

from fizzy import FizzyClient
from fizzy.auth import request_magic_link, submit_magic_code

# Request a magic link
request_magic_link("user@example.com")

# User receives email with 6-character code
session_token = submit_magic_code("ABC123")

# Use the session token
client = FizzyClient(
    session_token=session_token,
    account_slug="your-account-slug"
)

Resources

Identity

# Get authenticated user info (list of accounts)
identity = client.identity.get()
for account in identity.accounts:
    print(f"Account: {account.name} ({account.id})")
    print(f"  User: {account.user.name} - Role: {account.user.role}")

Boards

# List all boards
boards = client.boards.list()

# Get a specific board
board = client.boards.get("board-id")

# Create a board
board = client.boards.create(
    name="My Board",
    public_description="<p>Optional rich text description</p>"
)

# Update a board
board = client.boards.update("board-id", name="New Name")

# Delete a board
client.boards.delete("board-id")

Cards

# List cards with optional filters
cards = client.cards.list(
    board_id="board-123",     # Optional: filter by board
    column_id="col-456",      # Optional: filter by column
    tag_ids=["tag-1"],        # Optional: filter by tags
    assignee_ids=["user-1"],  # Optional: filter by assignees
    status="open"             # Optional: "open", "closed", "deferred"
)

# Get a specific card by number
card = client.cards.get(42)  # Cards are accessed by number, not ID

# Create a card
card = client.cards.create(
    board_id="board-123",
    title="New Card",
    description="<p>Rich text description</p>"  # HTML supported
)

# Create a card with header image
card = client.cards.create(
    board_id="board-123",
    title="Card with Image",
    image="/path/to/image.png"  # File path or tuple (filename, file_obj, content_type)
)

# Update a card
card = client.cards.update(42, title="Updated Title")

# Update card with new header image
card = client.cards.update(42, image="/path/to/new-image.png")

# Delete card header image
client.cards.delete_image(42)

# Delete a card
client.cards.delete(42)

# Card operations
client.cards.close(42)                          # Close the card
client.cards.reopen(42)                         # Reopen a closed card
client.cards.postpone(42)                       # Move to "not now"
client.cards.triage(42, column_id="col-123")    # Move to a column
client.cards.untriage(42)                       # Remove from triage
client.cards.toggle_tag(42, tag_title="Bug")    # Toggle a tag on/off
client.cards.toggle_assignment(42, assignee_id="user-123")  # Toggle assignment
client.cards.watch(42)                          # Start watching
client.cards.unwatch(42)                        # Stop watching
client.cards.gild(42)                           # Make a "golden ticket"
client.cards.ungild(42)                         # Remove golden status

Comments

# List comments on a card
comments = client.comments.list(42)  # card_number

# Get a specific comment
comment = client.comments.get(42, "comment-id")

# Create a comment
comment = client.comments.create(
    42,  # card_number
    body="<p>Hello, world!</p>"  # HTML supported
)

# Update a comment
comment = client.comments.update(42, "comment-id", body="<p>Updated</p>")

# Delete a comment
client.comments.delete(42, "comment-id")

Reactions

# List reactions on a comment
reactions = client.reactions.list(42, "comment-id")

# Add a reaction
reaction = client.reactions.create(42, "comment-id", content="thumbs_up")

# Remove a reaction
client.reactions.delete(42, "comment-id", "reaction-id")

Steps (Checklist Items)

# List steps (retrieved from the card)
steps = client.steps.list(42)  # card_number

# Get a specific step
step = client.steps.get(42, "step-id")

# Create a step
step = client.steps.create(42, content="Do this thing")

# Update a step (mark complete)
client.steps.update(42, "step-id", completed=True)

# Delete a step
client.steps.delete(42, "step-id")

Columns

# List columns on a board
columns = client.columns.list("board-id")

# Get a specific column
column = client.columns.get("board-id", "column-id")

# Create a column
column = client.columns.create("board-id", name="In Progress")

# Update a column
column = client.columns.update("board-id", "column-id", name="Done")

# Delete a column
client.columns.delete("board-id", "column-id")

Users

# List users in account
users = client.users.list()

# Get a specific user
user = client.users.get("user-id")

# Update a user (name or avatar)
user = client.users.update("user-id", name="New Name")
user = client.users.update("user-id", avatar="/path/to/avatar.png")

# Deactivate a user
client.users.delete("user-id")

Tags

# List all tags in account
tags = client.tags.list()

Notifications

# List notifications
notifications = client.notifications.list()

# Filter by read status
unread = client.notifications.list(read=False)

# Mark as read
client.notifications.mark_read("notification-id")

# Mark as unread
client.notifications.mark_unread("notification-id")

# Bulk mark as read
client.notifications.bulk_mark_read(["notif-1", "notif-2", "notif-3"])

File Uploads (Direct Uploads for Rich Text)

For embedding images in card descriptions or comments:

import hashlib
import base64

# Calculate MD5 checksum
with open("image.png", "rb") as f:
    content = f.read()
    checksum = base64.b64encode(hashlib.md5(content).digest()).decode()

# Create a direct upload
upload = client.uploads.create_direct_upload(
    filename="image.png",
    content_type="image/png",
    byte_size=len(content),
    checksum=checksum
)

# Upload to storage using the provided URL and headers
import httpx
httpx.put(
    upload.upload_url,
    content=content,
    headers=upload.upload_headers
)

# Build an ActionText attachment tag
from fizzy import DirectUpload
tag = DirectUpload.build_attachment_tag(upload.signed_id)

# Use in card description or comment
client.cards.update(42, description=f"<p>Check this out: {tag}</p>")
client.comments.create(42, body=f"<p>Here's an image: {tag}</p>")

Pagination

# Auto-pagination iterator
for card in client.cards.list_all(board_id="board-123"):
    print(card.title)

# Manual pagination
page = client.cards.list_paginated(board_id="board-123")
while page.has_next:
    for card in page.items:
        print(card.title)
    page = page.next_page()

Async Usage

from fizzy import AsyncFizzyClient
import asyncio

async def main():
    async with AsyncFizzyClient(
        token="your-token",
        account_slug="your-account-slug"
    ) as client:
        # All methods are async
        boards = await client.boards.list()

        # Async iteration
        async for card in client.cards.list_all():
            print(card.title)

asyncio.run(main())

Error Handling

from fizzy.exceptions import (
    FizzyError,           # Base exception
    AuthenticationError,  # 401
    ForbiddenError,       # 403
    NotFoundError,        # 404
    BadRequestError,      # 400
    RateLimitError,       # 429
    ServerError,          # 5xx
)

try:
    card = client.cards.get(99999)
except NotFoundError as e:
    print(f"Card not found: {e.message}")
except BadRequestError as e:
    print(f"Bad request: {e.message}")
except FizzyError as e:
    print(f"API error: {e.status_code} - {e.message}")

Caching

ETag caching is enabled by default for GET requests:

# First request stores the ETag
card = client.cards.get(123)

# Subsequent requests use If-None-Match header
# Returns cached response if server returns 304
card = client.cards.get(123)

# Disable caching
client = FizzyClient(
    token="...",
    account_slug="...",
    cache=False
)

Configuration

client = FizzyClient(
    token="your-token",
    account_slug="your-account-slug",
    base_url="https://app.fizzy.do",  # Default
    cache=True,                        # Enable ETag caching (default)
    timeout=30.0,                      # Request timeout in seconds
)

Development

# Clone the repository
git clone https://github.com/robzolkos/fizzy-client-python.git
cd fizzy-client-python

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run tests with coverage
pytest --cov=src/fizzy --cov-report=html

# Run linting
ruff check src tests
ruff format --check src tests

# Run type checking
mypy src

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT License - see the LICENSE file for details.

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

fizzy_api_client-0.1.0.tar.gz (22.6 kB view details)

Uploaded Source

Built Distribution

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

fizzy_api_client-0.1.0-py3-none-any.whl (35.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fizzy_api_client-0.1.0.tar.gz
  • Upload date:
  • Size: 22.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fizzy_api_client-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eb6d2dff4e2811df01cc41e3a8d2f9f6575f8922245e13ddcb79d8041c1e48f5
MD5 51b2e56ba59e413bccdbbfca4468587a
BLAKE2b-256 684b13010a507b8a04323faf4d0f3b84f52381c29856616bcd4b34e1d50717fa

See more details on using hashes here.

Provenance

The following attestation bundles were made for fizzy_api_client-0.1.0.tar.gz:

Publisher: release.yml on robzolkos/fizzy-client-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

File hashes

Hashes for fizzy_api_client-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 28b1a73cf5efa948f7efebfb9972ae3b6e19091212fb684a6828d540ae2cc4ab
MD5 cfc4e2d2159c2a3427f0e472fe7003c2
BLAKE2b-256 4e2a6b40ab53136aedd1f854ae0aee7e5a8fd9cabbd90ea21d7c5fa141106529

See more details on using hashes here.

Provenance

The following attestation bundles were made for fizzy_api_client-0.1.0-py3-none-any.whl:

Publisher: release.yml on robzolkos/fizzy-client-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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