> For the complete documentation index, see [llms.txt](https://docs.amigo.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amigo.ai/developer-guide/operations/reference/common-patterns.md).

# Common Patterns

Production-ready patterns for building with the Amigo AI API. Each pattern includes runnable Python and TypeScript examples.

{% hint style="info" %}
**Classic API.** These patterns use the Classic API SDKs and endpoints at `api.amigo.ai`. For Platform API equivalents, start with the [Platform SDK Quickstart](/developer-guide/platform-api/platform-sdk/quickstart.md).
{% endhint %}

## 1. Conversation Lifecycle Management

Create a conversation, send messages, handle streaming events, and finish cleanly.

```mermaid
%%{init: {"theme": "base", "themeVariables": {"actorBkg": "#083241", "actorTextColor": "#FFFFFF", "actorBorder": "#083241", "signalColor": "#575452", "signalTextColor": "#100F0F", "labelBoxBkgColor": "#F1EAE7", "labelBoxBorderColor": "#D7D2D0", "labelTextColor": "#100F0F", "loopTextColor": "#100F0F", "noteBkgColor": "#F1EAE7", "noteBorderColor": "#D7D2D0", "noteTextColor": "#100F0F", "activationBkgColor": "#E8E2EB", "activationBorderColor": "#083241", "altSectionBkgColor": "#F1EAE7", "altSectionColor": "#100F0F"}}}%%
sequenceDiagram
    participant App
    participant Amigo API
    App->>Amigo API: POST /conversation/ (NDJSON stream)
    Amigo API-->>App: conversation-created event
    Amigo API-->>App: agent greeting events
    App->>Amigo API: POST /conversation/{id}/interact (text)
    Amigo API-->>App: agent response events
    Amigo API-->>App: interaction-complete event
    App->>Amigo API: POST /conversation/{id}/finish/
```

{% tabs %}
{% tab title="Python" %}

```python
from amigo_sdk import AsyncAmigoClient
from amigo_sdk.models import (
    ConversationCreateConversationRequest,
    CreateConversationParametersQuery,
    InteractWithConversationParametersQuery,
    Format,
)

async def run_conversation():
    async with AsyncAmigoClient() as client:
        # 1. Create conversation (service_id goes in the request body)
        params = CreateConversationParametersQuery(
            response_format="text",
        )
        body = ConversationCreateConversationRequest(service_id="your-service-id")

        conversation_id = None
        async for event in await client.conversations.create_conversation(body, params):
            if hasattr(event, "conversation_id"):
                conversation_id = event.conversation_id
            # Handle greeting events...

        # 2. Interact
        interact_params = InteractWithConversationParametersQuery(
            request_format=Format.text,
            response_format="text",
        )
        async for event in await client.conversations.interact_with_conversation(
            conversation_id,
            interact_params,
            text_message="Hello, I need help with my account",
        ):
            print(event)

        # 3. Finish
        await client.conversations.finish_conversation(conversation_id)
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { AmigoClient, conversationId } from '@amigo-ai/sdk'

const client = new AmigoClient({
  apiKey: process.env.AMIGO_API_KEY!,
  apiKeyId: process.env.AMIGO_API_KEY_ID!,
  userId: process.env.AMIGO_USER_ID!,
  orgId: process.env.AMIGO_ORG_ID!,
})

// 1. Create conversation (service_id goes in the request body)
let convId: string | undefined
for await (const event of await client.conversations.createConversation({
  body: { service_id: 'your-service-id' },
  query: { response_format: 'text' },
})) {
  if ('conversation_id' in event) convId = event.conversation_id
}

// 2. Interact
for await (const event of await client.conversations.interactWithConversation({
  conversationId: conversationId(convId!),
  input: 'Hello, I need help with my account',
  query: { request_format: 'text', response_format: 'text' },
})) {
  console.log(event)
}

// 3. Finish
await client.conversations.finishConversation({ conversationId: conversationId(convId!) })
```

{% endtab %}
{% endtabs %}

**Key considerations:**

* Always call `finish_conversation` when done to release server resources.
* Handle the NDJSON stream event by event. Don't buffer the entire response.
* Use `abort_event` (Python) or `AbortController` (TypeScript) for cancellation.

***

## 2. User Creation and User Model Queries

Create users, let conversations enrich the user model, and query the result. To push facts from your own systems into the model, use `additional_context` - see [Enriching the User Model](/developer-guide/operations/reference/memory-architecture.md#enriching-the-user-model).

```mermaid
%%{init: {"theme": "base", "themeVariables": {"actorBkg": "#083241", "actorTextColor": "#FFFFFF", "actorBorder": "#083241", "signalColor": "#575452", "signalTextColor": "#100F0F", "labelBoxBkgColor": "#F1EAE7", "labelBoxBorderColor": "#D7D2D0", "labelTextColor": "#100F0F", "loopTextColor": "#100F0F", "noteBkgColor": "#F1EAE7", "noteBorderColor": "#D7D2D0", "noteTextColor": "#100F0F", "activationBkgColor": "#E8E2EB", "activationBorderColor": "#083241", "altSectionBkgColor": "#F1EAE7", "altSectionColor": "#100F0F"}}}%%
sequenceDiagram
    participant App
    participant Amigo API
    App->>Amigo API: POST /user/ (create user)
    Amigo API-->>App: User created
    App->>Amigo API: POST /conversation/ (with user)
    Note over Amigo API: Conversations enrich user model
    App->>Amigo API: GET /user/{user_id}/user_model
    Amigo API-->>App: Enriched user model
```

{% tabs %}
{% tab title="Python" %}

```python
from amigo_sdk import AmigoClient
from amigo_sdk.models import UserCreateInvitedUserRequest

client = AmigoClient()

# Create user. role_name must be a role returned by the Get roles endpoint.
# Optional fields: login_link, nonsensitive_user_variables,
# sensitive_user_variables, user_preferences.
user = client.users.create_user(
    UserCreateInvitedUserRequest(
        first_name="Jane",
        last_name="Doe",
        email="jane.doe@example.com",
        role_name="DefaultUserRole",
    )
)
print(f"Created user: {user.user_id}")

# After conversations have occurred, query the enriched model
model = client.users.get_user_model(user.user_id)
print(f"User model dimensions: {model}")
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
// Create user. role_name must be a role returned by the Get roles endpoint.
const user = await client.users.create({
  first_name: 'Jane',
  last_name: 'Doe',
  email: 'jane.doe@example.com',
  role_name: 'DefaultUserRole',
})
console.log('Created:', user.user_id)

// After conversations, query enriched model
const model = await client.users.getUserModel(userId(user.user_id))
console.log('User model:', model)
```

{% endtab %}
{% endtabs %}

***

## 3. Webhook-Driven Memory Sync

Receive conversation events via webhooks and sync memories to an external system.

```mermaid
%%{init: {"theme": "base", "themeVariables": {"actorBkg": "#083241", "actorTextColor": "#FFFFFF", "actorBorder": "#083241", "signalColor": "#575452", "signalTextColor": "#100F0F", "labelBoxBkgColor": "#F1EAE7", "labelBoxBorderColor": "#D7D2D0", "labelTextColor": "#100F0F", "loopTextColor": "#100F0F", "noteBkgColor": "#F1EAE7", "noteBorderColor": "#D7D2D0", "noteTextColor": "#100F0F", "activationBkgColor": "#E8E2EB", "activationBorderColor": "#083241", "altSectionBkgColor": "#F1EAE7", "altSectionColor": "#100F0F"}}}%%
sequenceDiagram
    participant Amigo API
    participant Webhook Endpoint
    participant External DB
    Amigo API->>Webhook Endpoint: POST /webhook (signed)
    Webhook Endpoint->>Webhook Endpoint: Verify HMAC signature
    Webhook Endpoint->>External DB: Store event data
    Webhook Endpoint-->>Amigo API: 200 OK
```

{% tabs %}
{% tab title="Python (FastAPI)" %}

```python
import hmac
import hashlib
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()
WEBHOOK_SECRET = "your-webhook-secret"

@app.post("/webhook")
async def handle_webhook(request: Request):
    body = await request.body()
    timestamp = request.headers.get("x-amigo-request-timestamp", "")
    signature = request.headers.get("x-amigo-request-signature", "")

    # Verify signature (constant-time comparison)
    payload = f"v1:{timestamp}:{body.decode()}"
    expected = hmac.new(
        WEBHOOK_SECRET.encode(), payload.encode(), hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        raise HTTPException(status_code=401, detail="Invalid signature")

    data = await request.json()
    event_type = data.get("type")

    if event_type == "conversation-post-processing-complete":
        # Sync updated memories to external system
        await sync_to_external_db(data)

    return {"status": "ok"}
```

{% endtab %}

{% tab title="TypeScript (Express)" %}

```typescript
import crypto from 'crypto'
import express from 'express'

const app = express()
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!

app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
  const timestamp = req.headers['x-amigo-request-timestamp'] as string
  const signature = req.headers['x-amigo-request-signature'] as string
  const body = req.body.toString()

  // Verify signature (constant-time comparison)
  const payload = `v1:${timestamp}:${body}`
  const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(payload).digest('hex')

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature')
  }

  const data = JSON.parse(body)
  // Process event...
  res.json({ status: 'ok' })
})
```

{% endtab %}
{% endtabs %}

**Key considerations:**

* Always use constant-time comparison for signature verification.
* Respond with 200 quickly and process events asynchronously if needed.
* Implement idempotency using the `x-amigo-idempotent-key` header, which stays constant across retries.
* Event payload shapes are documented in [Webhook Event Types](/developer-guide/classic-api/webhooks/webhook-events.md).

***

## 4. Multi-Service Routing

List available services and route conversations to the appropriate one based on user needs.

```mermaid
%%{init: {"theme": "base", "themeVariables": {"actorBkg": "#083241", "actorTextColor": "#FFFFFF", "actorBorder": "#083241", "signalColor": "#575452", "signalTextColor": "#100F0F", "labelBoxBkgColor": "#F1EAE7", "labelBoxBorderColor": "#D7D2D0", "labelTextColor": "#100F0F", "loopTextColor": "#100F0F", "noteBkgColor": "#F1EAE7", "noteBorderColor": "#D7D2D0", "noteTextColor": "#100F0F", "activationBkgColor": "#E8E2EB", "activationBorderColor": "#083241", "altSectionBkgColor": "#F1EAE7", "altSectionColor": "#100F0F"}}}%%
sequenceDiagram
    participant User
    participant Router
    participant Amigo API
    User->>Router: Request help
    Router->>Amigo API: GET /service/ (list services)
    Amigo API-->>Router: Available services
    Router->>Router: Select best service
    Router->>Amigo API: POST /conversation/ (with selected service)
    Amigo API-->>Router: Conversation stream
    Router-->>User: Agent response
```

{% tabs %}
{% tab title="Python" %}

```python
from amigo_sdk import AmigoClient
from amigo_sdk.models import GetServicesParametersQuery

client = AmigoClient()

# List all services
services = client.services.get_services(GetServicesParametersQuery())

# Route based on user intent
def select_service(user_intent: str, services):
    for svc in services.services:
        if user_intent.lower() in svc.description.lower():
            return svc.id
    return services.services[0].id  # fallback to first

service_id = select_service("billing question", services)
# Now create conversation with the selected service_id
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const services = await client.services.list()

function selectService(intent: string) {
  const match = services.services?.find(s =>
    s.description?.toLowerCase().includes(intent.toLowerCase())
  )
  return match?.id ?? services.services?.[0]?.id
}

const serviceId = selectService('billing question')
```

{% endtab %}
{% endtabs %}

***

## 5. Simulation-Driven Deployment

Run automated simulations before deploying agent changes to production. Unlike the patterns above, this one uses the Agent Forge CLI (`forge`) rather than the SDKs - the CLI wraps the simulation APIs and is the recommended way to gate deployments in CI.

```mermaid
%%{init: {"theme": "base", "themeVariables": {"actorBkg": "#083241", "actorTextColor": "#FFFFFF", "actorBorder": "#083241", "signalColor": "#575452", "signalTextColor": "#100F0F", "labelBoxBkgColor": "#F1EAE7", "labelBoxBorderColor": "#D7D2D0", "labelTextColor": "#100F0F", "loopTextColor": "#100F0F", "noteBkgColor": "#F1EAE7", "noteBorderColor": "#D7D2D0", "noteTextColor": "#100F0F", "activationBkgColor": "#E8E2EB", "activationBorderColor": "#083241", "altSectionBkgColor": "#F1EAE7", "altSectionColor": "#100F0F"}}}%%
sequenceDiagram
    participant CI
    participant Forge as Agent Forge CLI
    participant Amigo API
    CI->>Forge: forge conversation simulate
    Forge->>Amigo API: Run simulated conversation + metrics
    Amigo API-->>Forge: Simulation results
    Forge-->>CI: Exit code (pass/fail)
    alt Exit code 0
        CI->>CI: Deploy to production
    else Non-zero exit code
        CI->>CI: Block deployment
    end
```

{% tabs %}
{% tab title="Python" %}

```python
import subprocess
import sys

def run_simulation_gate():
    """CI gate that runs simulations before deploy."""
    # Use Agent Forge CLI for simulation
    result = subprocess.run(
        [
            "forge", "conversation", "simulate",
            "-s", "My Service",
            "-e", "staging",
            "--test-user",
            "--analyze-wait", "30",
            "--max-turns", "10",
        ],
        capture_output=True, text=True,
    )

    if result.returncode != 0:
        print(f"Simulation failed:\n{result.stderr}")
        sys.exit(1)

    print("Simulation passed. Safe to deploy.")

if __name__ == "__main__":
    run_simulation_gate()
```

{% endtab %}

{% tab title="GitHub Actions" %}

```yaml
jobs:
  simulation-gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Agent Forge
        run: curl -fsSL https://forge.platform.amigo.ai/install.sh | sh
      - name: Run simulations
        env:
          API_KEY: ${{ secrets.AMIGO_API_KEY }}
          API_KEY_ID: ${{ secrets.AMIGO_API_KEY_ID }}
        run: |
          forge conversation simulate \
            -s "${{ vars.SERVICE_NAME }}" \
            -e staging \
            --test-user \
            --max-turns 10
      - name: Deploy if passed
        if: success()
        run: echo "Deploying..."
```

{% endtab %}
{% endtabs %}

**Key considerations:**

* Use `--test-user` for parallel simulation execution without conflicts.
* Set `--analyze-wait` to allow time for metrics computation after simulation.
* Define clear pass/fail thresholds for your metrics (for example, safety >= 9.0).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.amigo.ai/developer-guide/operations/reference/common-patterns.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
