> ## Documentation Index
> Fetch the complete documentation index at: https://developers.meshapi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Apigee

> Route Mesh API traffic through your own Apigee proxy — the target settings you must change, what Apigee cannot carry, and how to verify the path end to end.

If your organisation standardises ingress on **Google Apigee**, you can put a
pass-through proxy in front of Mesh API and keep a single point where
authentication, quotas, analytics and traffic policy are applied. Mesh API
needs no change to support this, and neither does your client code — a
correctly configured proxy is transparent, and every endpoint behaves
identically through it and direct.

"Correctly configured" is the whole job. Several Apigee target defaults break
Mesh traffic — streaming responses get buffered, slow generation calls time
out, and Mesh's own error bodies get replaced by Apigee fault envelopes. This
page covers the settings that matter, the traffic Apigee cannot carry at all,
and how to prove the path works.

<Note>
  Mesh's own docs use **gateway** for the Mesh router itself. On this page,
  **the proxy** always means your Apigee proxy, and **Mesh API** means the
  backend behind it.
</Note>

## How the pieces fit

Client traffic terminates at Apigee, which forwards to Mesh API and returns the
response unchanged. The proxy does not rewrite paths, alter request or response
bodies, or inject its own authentication.

```mermaid theme={null}
flowchart LR
    C[Your client] --> A[Apigee proxy<br/>/meshapi]
    A --> M[Mesh API<br/>api.meshapi.ai]
    A -.-> P[auth · quotas · rate limits<br/>analytics · logging]
```

Use a **single catch-all route**. The path suffix after your base path is
forwarded verbatim:

```
https://your-apigee-host/meshapi/v1/chat/completions
                                └─────────┬────────┘
                                          │  forwarded as-is
                                          ▼
                    https://api.meshapi.ai/v1/chat/completions
```

Because routing is one catch-all rule rather than a route per endpoint, **new
Mesh API endpoints work through your proxy with no proxy change**. Given how
often the catalog gains capabilities, per-endpoint routing is a standing
maintenance cost with no benefit.

## Base URL

Migrating an existing integration is a base-URL change only. Paths, methods,
headers, request bodies and response shapes are all unchanged.

```diff theme={null}
- https://api.meshapi.ai/v1/chat/completions
+ https://your-apigee-host/meshapi/v1/chat/completions
```

This works with the Mesh SDKs too — set `base_url` to your proxy and everything
else stays as documented in the [SDK reference](/sdk/overview).

## Required target settings

These are the settings that differ from Apigee's defaults. Each one is required,
and the last three all cause failures that look like Mesh problems but are not.

| Setting                      | Value                 | Why                                                           |
| ---------------------------- | --------------------- | ------------------------------------------------------------- |
| `request.streaming.enabled`  | `true`                | SSE on chat completions; multipart audio uploads              |
| `response.streaming.enabled` | `true`                | SSE responses; raw audio from `/v1/audio/speech`              |
| `io.timeout.millis`          | `180000`              | The \~55 s default is shorter than image and video generation |
| `connect.timeout.millis`     | `10000`               |                                                               |
| `success.codes`              | `1xx,2xx,3xx,4xx,5xx` | Relay Mesh's real `401`/`404` instead of wrapping them        |
| Base path                    | `/meshapi`            | Any value; must match your client base URL                    |
| Route                        | single catch-all      | New endpoints need no proxy change                            |

<Warning>
  Without the two streaming properties, Apigee buffers the response — SSE
  arrives in one lump after the generation finishes instead of token by token,
  and binary audio from `/v1/audio/speech` breaks outright.

  Without `success.codes`, error semantics change at the proxy: your callers
  receive Apigee fault envelopes rather than [Mesh's error
  bodies](/docs/reference/api-overview), so existing error handling stops
  matching.
</Warning>

Start with a bare pass-through and no policies, so that any behaviour change is
attributable to the proxy itself. Add quotas, SpikeArrest, caching and
API-product keys one at a time afterwards.

## Authentication

The proxy passes the `Authorization` header straight through. Use the same
Mesh API key you would use directly:

```http theme={null}
Authorization: Bearer rsk_...
```

| Condition                 | Response                            |
| ------------------------- | ----------------------------------- |
| No `Authorization` header | `401` with Mesh's own error body    |
| Invalid key               | `401`                               |
| Valid key                 | `200`, response identical to direct |

Errors are relayed from Mesh unmodified — provided `success.codes` is set as
above.

If you want consumers to hold Apigee-issued credentials instead of a Mesh key,
that is an API product and developer app on top of this proxy, with the Mesh
key injected at the target. Set up the pass-through first and verify it, then
layer that on.

## Verify the path

<Steps>
  <Step title="Send a request through the proxy">
    ```bash theme={null}
    curl --request POST \
      --url https://your-apigee-host/meshapi/v1/chat/completions \
      --header 'Authorization: Bearer rsk_...' \
      --header 'Content-Type: application/json' \
      --data '{
        "model": "anthropic/claude-haiku-4.5",
        "messages": [{"role": "user", "content": "Say hi in one sentence."}],
        "temperature": 0.2
      }'
    ```

    **Response:**

    ```json theme={null}
    {
      "id": "chatcmpl-019fdcb94a4d765eb0cd",
      "object": "chat.completion",
      "model": "anthropic/claude-haiku-4.5",
      "choices": [{
        "index": 0,
        "message": {"role": "assistant", "content": "Hi! How can I help you today?"},
        "finish_reason": "stop"
      }],
      "usage": {"prompt_tokens": 13, "completion_tokens": 12, "total_tokens": 25}
    }
    ```
  </Step>

  <Step title="Send the same request direct, and diff">
    Point the same call at `https://api.meshapi.ai` and compare. A response that
    differs in anything but `id` and timing means the proxy is transforming
    traffic it should be passing through.

    Keep this pair as your regression test. If behaviour changes later, running
    both isolates the cause at once — a response that differs only through the
    proxy points at your proxy configuration, and one that matches direct points
    somewhere else entirely.
  </Step>

  <Step title="Check streaming separately">
    Re-send with `"stream": true` and confirm chunks arrive progressively rather
    than all at once at the end. Buffering is the single most common
    misconfiguration and a non-streaming test will not catch it.
  </Step>
</Steps>

## What Apigee cannot carry

### Realtime audio over WebSocket

```
wss://api.meshapi.ai/v1/audio/transcriptions/realtime
```

**Apigee does not proxy WebSocket traffic.** This is a platform limitation, not
a configuration issue — there is no proxy setting that enables it.

[Realtime transcription](/docs/capabilities/realtime-audio) clients must connect
**directly to Mesh API**, bypassing the proxy. Any network policy that assumes
all traffic flows through Apigee needs an explicit exception for this endpoint.

### File uploads bypass the proxy

`POST /v1/files` returns a pre-signed Google Cloud Storage URL:

```json theme={null}
{
  "file_id": "019fdcb7-756c-6c46-82fe-fb290d0a6533",
  "signed_url": "https://storage.googleapis.com/mesh-rag-files-prod/..."
}
```

Your client then `PUT`s the bytes **directly to `storage.googleapis.com`**. That
transfer does not pass through Apigee, so proxy policies, quotas and analytics
apply only to the metadata call that reserves the record — never to file
content. Egress rules must allow the storage host. See [Files &
RAG](/docs/capabilities/rag) for the full upload flow.

### Payloads above \~10 MB

Apigee buffers messages with a default ceiling of roughly **10 MB**. Typical
Mesh payloads sit comfortably inside it:

| Payload                         | Size    |
| ------------------------------- | ------- |
| `GET /v1/models` response       | 2.16 MB |
| Image generation response       | 1.63 MB |
| `GET /v1/audio/voices` response | 276 KB  |

Beyond that, requests may be rejected at the proxy with a `413` that does not
occur direct. Large file content should use the pre-signed upload flow above
rather than inline payloads.

### Slow calls against the default timeout

Generation endpoints routinely exceed Apigee's default target timeout, which is
why `io.timeout.millis` is on the required list. Observed durations through a
pass-through proxy:

| Operation                       | Time      |
| ------------------------------- | --------- |
| Image edits                     | \~33 s    |
| Image generation                | \~28 s    |
| Chat compare, 3 upstream models | \~8 s     |
| Web search                      | \~5 s     |
| Metadata reads                  | \< 150 ms |

[Video generation](/docs/capabilities/video-generation) is asynchronous — you
poll a task, so no single call is long-running. A `504` from the proxy on a call
that succeeds direct means this ceiling needs raising.

## Errors that are not proxy-related

Three Mesh API responses are easy to mistake for proxy faults during a cutover.
Each reproduces identically against the backend direct, so diffing the two calls
identifies them straight away.

| Response                     | What it means                                                                                                                                                         |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `404 model_not_found`        | Model IDs are brand-namespaced — `anthropic/claude-haiku-4.5`, not `claude-haiku-4.5`. Use the exact `id` from `GET /v1/models`; see [Models](/docs/reference/models) |
| `422` on usage endpoints     | Usage endpoints take `org_id` as a required parameter. Pass it explicitly on every call; see [Usage API](/docs/reference/usage-api)                                   |
| `400 Disallowed CORS origin` | Browser origins are allowlisted. A proxy in front of Mesh changes the `Origin` your callers present, so the new origin needs adding                                   |

Only the last is affected by the proxy at all. If you serve browser clients
through Apigee, allowlist the proxy-facing origin before you cut over.

## Verified coverage

Mesh verifies every documented endpoint through a pass-through Apigee proxy and
against the backend direct, then diffs the two. **No endpoint behaves
differently through the proxy** — every group returns responses matching direct:

| Group          | Endpoints | Notes                                        |
| -------------- | --------- | -------------------------------------------- |
| Chat           | 2         | Includes SSE streaming                       |
| Core inference | 4         | Responses, messages, embeddings, moderations |
| Images         | 2         | Generation, edits                            |
| Video          | 3         | Asynchronous job model                       |
| Audio          | 6 HTTP    | Excludes the WebSocket endpoint              |
| Batches        | 4         | Create, get, cancel, list                    |
| Files          | 5         | Init, status, embed, search, list            |
| Memories       | 12        | Full CRUD including items                    |
| Templates      | 5         | Full CRUD                                    |
| Models         | 3         |                                              |
| Usage          | 5         |                                              |
| Audit logs     | 2         | Includes CSV export                          |
| Router         | 1         |                                              |
| Account        | 2         | Balance, error catalog                       |
| Web            | 1         |                                              |

The realtime audio WebSocket is the sole exclusion, for the reason above.

## Troubleshooting

| Symptom                                       | Likely cause                               | Action                                         |
| --------------------------------------------- | ------------------------------------------ | ---------------------------------------------- |
| TCP connects, TLS handshake dropped           | Load balancer certificate not yet `ACTIVE` | `gcloud compute ssl-certificates describe`     |
| SSE arrives all at once                       | Response buffering                         | Confirm `response.streaming.enabled=true`      |
| `504` at the proxy, works direct              | Target timeout too low                     | Raise `io.timeout.millis`                      |
| `413` at the proxy, works direct              | Payload over \~10 MB                       | Use the pre-signed upload flow                 |
| Apigee fault envelope instead of a Mesh error | `success.codes` not set                    | Set it to `1xx,2xx,3xx,4xx,5xx`                |
| WebSocket fails to connect                    | Apigee cannot proxy WS                     | Connect direct to Mesh API                     |
| `400 Disallowed CORS origin`                  | Origin not allowlisted                     | Add the proxy-facing origin                    |
| A new endpoint 404s at the proxy              | Route is not catch-all                     | Replace per-endpoint routes with one catch-all |

Anything that reproduces against `https://api.meshapi.ai` directly is not a
proxy issue — see [Troubleshooting](/debug/mesh-api) or contact
[support](/docs/reference/support).
