# API Usage Best Practices

This guide covers best practices for using the PizzaStack API efficiently and correctly.

## Error Handling

### Always Check Response Status

```python
import requests

API_BASE = "https://api.tomatopy.pizza/v1"
HEADERS = {
    "Content-Type": "application/json",
    "X-API-Key": "your-api-key",
    "X-Session-ID": "your-session-id"
}

# GOOD: Check status and handle errors
response = requests.post(f"{API_BASE}/tomato/acquire", headers=HEADERS, json={
    "variety": "San Marzano",
    "ripeness": 0.8,
    "weight": 150
})

if response.status_code == 200:
    tomato = response.json()
elif response.status_code == 400:
    print(f"Bad request: {response.json()['message']}")
elif response.status_code == 401:
    print("Invalid API key")
else:
    print(f"Unexpected error: {response.status_code}")
```

### Use raise\_for\_status()

For simpler scripts, use `raise_for_status()` to throw exceptions on errors:

```python
# GOOD: Exceptions on error
try:
    response = requests.post(f"{API_BASE}/tomato/acquire", headers=HEADERS, json={
        "variety": "San Marzano",
        "ripeness": 0.8,
        "weight": 150
    })
    response.raise_for_status()
    tomato = response.json()
except requests.exceptions.HTTPError as e:
    print(f"API error: {e}")
except requests.exceptions.ConnectionError:
    print("Could not connect to API")
```

### Create a Helper Function

Reduce boilerplate with a wrapper:

```python
def api_post(endpoint, payload):
    """Make a POST request to the PizzaStack API with error handling."""
    response = requests.post(f"{API_BASE}{endpoint}", headers=HEADERS, json=payload)
    response.raise_for_status()
    return response.json()

# Usage
tomato = api_post("/tomato/acquire", {
    "variety": "San Marzano",
    "ripeness": 0.8,
    "weight": 150
})
```

## Checking Response Quality Fields

Sauce responses include a `sauce_quality` field. Always check it to catch pipeline issues early.

```python
# GOOD: Check quality at each step
tomato = api_post("/tomato/acquire", {
    "variety": "San Marzano", "ripeness": 0.9, "weight": 400
})
print(f"Tomato type: {tomato['type']}")

sliced = api_post("/tomato/slice", {
    "tomato_ids": [tomato["id"]], "method": "dice",
    "size": "medium", "consistency": "uniform"
})

sauce = api_post("/cook/simmer", {
    "ingredients": [{"id": sliced["id"], "name": "tomato", "amount": 400, "unit": "g"}],
    "temperature": 100, "duration": "30m"
})

# Catch quality degradation early
if sauce["sauce_quality"] == "degraded":
    print("WARNING: Sauce quality is low. Did you slice the tomatoes first?")
```

## Validating Pipeline Order

The PizzaStack pipeline has strict ordering requirements. Two key rules to follow:

### Rule 1: Slice Before Simmering

Raw tomato IDs passed to `/cook/simmer` will not cause an error, but the sauce quality will silently degrade.

```python
# GOOD: Slice first
sliced = api_post("/tomato/slice", {
    "tomato_ids": [tomato["id"]],
    "method": "dice",
    "size": "medium",
    "consistency": "uniform"
})
sauce = api_post("/cook/simmer", {
    "ingredients": [{"id": sliced["id"], "name": "tomato", "amount": 400, "unit": "g"}],
    "temperature": 100,
    "duration": "30m"
})

# BAD: Skip slicing -- quality degrades silently
sauce = api_post("/cook/simmer", {
    "ingredients": [{"id": tomato["id"], "name": "tomato", "amount": 400, "unit": "g"}],
    "temperature": 100,
    "duration": "30m"
})
# sauce["sauce_quality"] will be "degraded"
```

### Rule 2: Assemble Before Baking

Passing a `base_id` to `/pizza/bake` returns a 400 error. You must assemble first.

```python
# GOOD: Assemble then bake
pizza = api_post("/pizza/assemble", {
    "base_id": base["id"],
    "sauce_id": sauce["id"],
    "toppings": [{"name": "mozzarella", "amount": 200, "unit": "g"}]
})
baked = api_post("/pizza/bake", {
    "pizza_id": pizza["id"],
    "temperature": 450,
    "duration": "2m"
})

# BAD: Try to bake a base directly -- returns 400
try:
    baked = api_post("/pizza/bake", {
        "pizza_id": base["id"],  # Wrong! This is a base_id
        "temperature": 450,
        "duration": "2m"
    })
except requests.exceptions.HTTPError as e:
    print(f"Error: {e}")  # 400 Bad Request
```

## Reusing Session IDs Correctly

### Do: Use the Same Session for Related Operations

```python
import uuid

# All operations in one pizza workflow share a session
session_id = str(uuid.uuid4())
HEADERS["X-Session-ID"] = session_id

tomato = api_post("/tomato/acquire", {...})
sliced = api_post("/tomato/slice", {"tomato_ids": [tomato["id"]], ...})
sauce = api_post("/cook/simmer", {"ingredients": [{"id": sliced["id"], ...}], ...})
# All objects are in the same session and can reference each other
```

### Do Not: Mix Unrelated Workflows in One Session

```python
# BAD: Two unrelated pizzas in one session creates confusion
marinara_tomato = api_post("/tomato/acquire", {"variety": "San Marzano", ...})
pesto_basil = api_post("/tomato/acquire", {"variety": "Roma", ...})
# Which tomato goes with which pizza? Hard to track.
```

### Do Not: Change Session ID Mid-Workflow

```python
# BAD: Changing session mid-workflow breaks references
HEADERS["X-Session-ID"] = str(uuid.uuid4())
tomato = api_post("/tomato/acquire", {...})

HEADERS["X-Session-ID"] = str(uuid.uuid4())  # New session!
sliced = api_post("/tomato/slice", {
    "tomato_ids": [tomato["id"]],  # This ID is from the old session -- will fail
    ...
})
```

## Retry Logic

For production use, add retry logic for transient errors:

```python
import time

def api_post_with_retry(endpoint, payload, max_retries=3):
    """Make API call with retry logic for transient errors."""
    for attempt in range(max_retries):
        try:
            response = requests.post(
                f"{API_BASE}{endpoint}",
                headers=HEADERS,
                json=payload,
                timeout=30
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.ConnectionError:
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # Exponential backoff
                continue
            raise
        except requests.exceptions.HTTPError as e:
            if response.status_code >= 500 and attempt < max_retries - 1:
                time.sleep(2 ** attempt)
                continue
            raise
```

## Best Practices Summary

1. **Always handle errors** -- check status codes or use `raise_for_status()`
2. **Check quality fields** -- catch pipeline issues before they cascade
3. **Follow pipeline order** -- slice before simmer, assemble before bake
4. **One session per workflow** -- do not mix unrelated operations
5. **Keep session IDs consistent** -- do not change mid-workflow
6. **Add retry logic** for production applications
7. **Use a helper function** to reduce boilerplate

## Next Steps

* [Session Management Best Practices](/docs/best-practices/virtual-kitchen-management.md) - Session lifecycle and debugging
* [Core API Endpoints](/docs/core-modules.md) - Endpoint reference
* [Tutorials](/docs/tutorials/perfect-pizza-production.md) - End-to-end examples


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tomatopy.pizza/docs/best-practices/code-efficiency.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
