Five-Minute Quickstart
Send one real, low-risk request through Punk, then inspect the exact run that served it. This is the hosted path; the gateway origin is always https://app.punktechnologies.com.
For an enterprise rollout, use Enterprise Pilot after this first run. For the complete protocol and integration variants, continue with First Run, OpenAI Gateway, or Anthropic SDK.
1. Create the safe first-run setup
- Sign up at https://punktechnologies.com/signup and verify the email from
- In
https://app.punktechnologies.com, open Governance → API keys and
Verify your email for Punk. Verification activates the trial's effective plan and quotas; it is not an API-key substitute.
create a key with these first-run settings:
| Setting | Value |
|---|---|
| Name | support-app-observe |
| Mode | observe |
| App id | support-app |
| Admin | off |
Copy the pk_... token now. The app-pinned key keeps this integration scoped to support-app; it overrides any X-Punk-App value sent by the client. A non-admin observe key records traffic and always returns the live result—it cannot make an optimized route serve or enforce a blocking policy.
- Choose where the live answer comes from before sending a model request:
| Response source | Choose it when | Setup |
|---|---|---|
| Platform provider | Your organization is entitled to use a provider already configured for its Punk workspace. | Pick a supported model for that provider; no provider secret belongs in your app. |
| BYOK provider | Your organization supplies its own OpenAI, Anthropic, OpenRouter, DeepSeek, or Moonshot/Kimi credential. | Add the credential in Governance → Provider keys. Keep it out of application environment variables. |
gpt-4o-mini needs an available OpenAI-compatible source; claude-sonnet-4-6 needs an available Anthropic source. A Punk tenant key authenticates your app to Punk—it is not an OpenAI or Anthropic key.
export PUNK_BASE_URL=https://app.punktechnologies.com
export PUNK_API_KEY=pk_...
2. Send one request
Use one of the equivalent examples below. Keep all three identity dimensions stable for this first check. X-Punk-Subject should be a pseudonymous user, account, ticket, or work-item id—not an email address or customer name.
curl
curl -sS -D /tmp/punk-headers -o /tmp/punk-body \
"$PUNK_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $PUNK_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Punk-App: support-app" \
-H "X-Punk-Agent: triage-worker" \
-H "X-Punk-Subject: ticket-123" \
-d '{
"model": "gpt-4o-mini",
"temperature": 0,
"messages": [{
"role": "user",
"content": "Classify this ticket: I was charged twice. Return JSON."
}]
}'
RUN_ID=$(awk 'tolower($1)=="x-punk-run-id:" {print $2}' /tmp/punk-headers | tr -d '\r' | tail -n 1)
ROUTE=$(awk 'tolower($1)=="x-punk-route:" {print $2}' /tmp/punk-headers | tr -d '\r' | tail -n 1)
jq -r '.choices[0].message.content' /tmp/punk-body
printf 'route=%s runId=%s\n' "$ROUTE" "$RUN_ID"
OpenAI SDK
npm install openai
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://app.punktechnologies.com/v1",
apiKey: process.env.PUNK_API_KEY,
defaultHeaders: {
"X-Punk-App": "support-app",
"X-Punk-Agent": "triage-worker",
"X-Punk-Subject": "ticket-123",
},
});
const { data, response } = await client.chat.completions.create({
model: "gpt-4o-mini",
temperature: 0,
messages: [{ role: "user", content: "Classify this ticket: I was charged twice. Return JSON." }],
}).withResponse();
console.log(data.choices[0]?.message?.content);
console.log({
route: response.headers.get("x-punk-route"),
runId: response.headers.get("x-punk-run-id"),
});
Anthropic SDK
npm install @anthropic-ai/sdk @punktechnologies/sdk
import Anthropic from "@anthropic-ai/sdk";
import { createPunkAnthropicConfig } from "@punktechnologies/sdk/anthropic";
const client = new Anthropic(createPunkAnthropicConfig({
baseUrl: "https://app.punktechnologies.com",
apiKey: process.env.PUNK_API_KEY,
app: "support-app",
agent: "triage-worker",
subject: "ticket-123",
}));
const { data, response } = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 256,
messages: [{ role: "user", content: "Classify this ticket: I was charged twice. Return JSON." }],
}).withResponse();
console.log(data.content);
console.log({
route: response.headers.get("x-punk-route"),
runId: response.headers.get("x-punk-run-id"),
});
The Anthropic client receives the gateway origin (no /v1 suffix); it adds /v1/messages. The OpenAI client receives the /v1 base URL.
Punk TypeScript SDK
npm install @punktechnologies/sdk
import { Punk } from "@punktechnologies/sdk";
const punk = new Punk({
baseUrl: "https://app.punktechnologies.com",
apiKey: process.env.PUNK_API_KEY,
app: "support-app",
agent: "triage-worker",
subject: "ticket-123",
});
const result = await punk.chat({
model: "gpt-4o-mini",
temperature: 0,
messages: [{ role: "user", content: "Classify this ticket: I was charged twice. Return JSON." }],
});
console.log(result.content);
console.log({ route: result.route, runId: result.runId });
const detail = await punk.runDetail(result.runId);
console.dir(detail.run.routeExplanation, { depth: null });
3. Read the first result
With a working live provider and this observe key, the first response normally has x-punk-route: live. The run explanation should say that Punk selected live pass-through, record the provider/model and identity, and describe any cache miss or not-yet-eligible alternatives. live on the first run is the expected safe result, not a failed optimization.
Look up the exact run from any example:
curl -sS -H "Authorization: Bearer $PUNK_API_KEY" \
"$PUNK_BASE_URL/api/v1/runs/$RUN_ID" \
| jq '{
id: .run.id,
route: .run.route,
provider: .run.provider,
model: .run.model,
app: .run.appId,
agent: .run.agentId,
subject: .run.subject,
explanation: .run.routeExplanation
}'
Open Runs in the dashboard and select the same run id. This ordinary run detail is the route explanation for every model request. A Chorus receipt is separate and is only expected for model: "punk/chorus"; retrieve one, when requested, at GET /api/v1/runs/:runId/receipt. Do not use a missing Chorus receipt to diagnose a normal gpt-* or claude-* run.
Integration diagnostics
| Symptom | Check | Fix |
|---|---|---|
401 | Request has no Authorization: Bearer pk_..., or the token was copied only partially. | Create or copy a current Punk tenant key. Do not use an OpenAI/Anthropic key at the gateway. |
| No Punk run id | OpenAI requests must use https://app.punktechnologies.com/v1; Anthropic and Punk SDK baseUrl use https://app.punktechnologies.com. | Remove an accidental duplicate /v1, proxy URL, or direct provider base URL. |
Provider error or mock provider | The selected model family has no usable platform provider/BYOK key, or local development explicitly forces PUNK_PROVIDER=mock. | Add/select the matching provider source in Governance → Provider keys; in a local environment remove the mock setting when checking live traffic. An admin can use GET /api/v1/readiness; a non-admin app key correctly receives 403 there. |
| Model rejected | gpt-* and claude-* use different provider families. | Use a model supported by the configured source, for example gpt-4o-mini for OpenAI-compatible or claude-sonnet-4-6 for Anthropic. |
| Quota or plan error | Open Usage & billing and confirm the active organization, verified trial status, and remaining run quota. | Verify the signup email, switch to the intended organization, or change the plan/quota with an owner or admin. GET /api/v1/usage shows the same tenant usage. |
| Wrong app/agent/subject in the run | Inspect the exact run lookup above. | Send all X-Punk-* dimensions or constructor options. For an app-pinned key, use its pinned app id; Punk intentionally overrides a conflicting app header. |
Once this run is clear, repeat the same low-risk request and use First Run for the longer observe → evidence → optimize path.