Mangrove Profiles
URL-addressed request presets — a pinned model and stored instructions behind a plain OpenAI request body.
A Mangrove profile is a request preset stored with your account: a model choice plus a set of instructions, addressed by its own URL. Sending a chat completion to
POST /v1/agent-profiles/{slug}/chat/completionsapplies the profile server-side — the pinned model is written onto the request and the stored instructions join the system message. The body stays the plain OpenAI chat-completion contract, so a thin client — a shell alias, a phone automation, a no-code tool that only knows "OpenAI-compatible" — sends nothing but messages.
Not the same as Mangrove runs
Mangrove runs are runs that work through a task with web search, document analysis, or transcription. A Mangrove profile changes nothing about how a request is executed — it is a stored configuration for a regular chat completion, and that is all.
Calling a profile
The endpoint ends in /chat/completions, so a stock OpenAI SDK needs no
request changes at all: point its base URL at the profile and the SDK
appends the rest. Authentication is your API key, as on every endpoint.
from openai import OpenAI
client = OpenAI(
api_key=os.environ["GREENPT_API_KEY"],
base_url="https://api.greenpt.ai/v1/agent-profiles/support-triage",
)
client.chat.completions.create(
model="", # the profile pins the model; "" satisfies the SDK
messages=[{"role": "user", "content": "Customer says the export hangs."}],
)import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.GREENPT_API_KEY,
baseURL: 'https://api.greenpt.ai/v1/agent-profiles/support-triage',
});
await client.chat.completions.create({
model: '', // the profile pins the model; '' satisfies the SDK
messages: [{ role: 'user', content: 'Customer says the export hangs.' }],
});curl https://api.greenpt.ai/v1/agent-profiles/support-triage/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [{ "role": "user", "content": "Customer says the export hangs." }]
}'Streaming works as on the plain endpoint — set "stream": true and read
the streamed response as usual.
Profiles are addressed by URL only. The request body never names one: an
agent body field is not part of the contract, so — on this endpoint and on
plain /v1/chat/completions alike — it travels to the upstream model
provider and is rejected there like any other unknown parameter.
The profile pins the model
The model parameter has three accepted forms, because the profile owns
the choice:
- omitted — the usual form for raw HTTP clients;
""— satisfies SDKs that require the parameter;- exactly the pinned model — a redundant repeat is fine.
Any other value is a contradiction, and the request fails with a 400
naming the pinned model rather than silently overriding either side:
The agent profile pins the model; omit "model", send "", or send "gemma4".How a profile is applied
- Resolve — the slug is matched against your own profiles, exactly and
case-sensitively. Profiles are strictly personal: there is no
organisation or GreenPT catalog, and nobody else's profile is reachable
through your key. A slug that does not match an enabled profile of yours
fails the request with a
404. - Pin — the profile's model is written onto the request. Whether that model id is servable is judged afterwards, exactly as if you had sent it yourself.
- Inject — the stored instructions join the request's system message, wrapped in the same fenced block as skills: guidance you chose for the request, subordinate to the instructions outside it. A request without a system message gets one holding the block.
- Bill — the injected block's tokens are input tokens like any others,
included in the
usagethe response reports; theX-Agent-Profile-Injected-Tokensheader tells you exactly how many.
The skills request field keeps working on this endpoint as it does on the
plain one — a profile fixes the model and base instructions, and a skill
can still be named per request on top.
Response headers
Responses from the profile endpoint carry three headers, on streaming and non-streaming responses alike:
| Header | Meaning |
|---|---|
X-Agent-Profile | The applied profile's slug. |
X-Agent-Profile-Model | The model the profile pinned onto the request. |
X-Agent-Profile-Injected-Tokens | Tokens the injected block added to your prompt, wrapper included. |
Managing profiles
Profiles are created and maintained at /v1/agent-profiles, authenticated
with your API key. Every profile you can see is your own.
The profile object
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier, used in the management routes. |
slug | string | The URL segment that addresses the profile. |
model | string | The model pinned onto every completion through the profile. |
instructions | string | The instructions injected when the profile is applied. Detail and write responses only. |
enabled | boolean | Whether the profile's completion endpoint resolves. |
createdAt | string | ISO 8601 timestamp. |
updatedAt | string | ISO 8601 timestamp. |
List profiles
GET /v1/agent-profilesReturns { "agentProfiles": [...] } with your profiles, enabled or not,
sorted by slug. List entries omit instructions; fetch a profile by id for
the full object. The list is not paginated.
Get a profile
GET /v1/agent-profiles/{id}Returns the full profile object, including instructions.
Create a profile
POST /v1/agent-profiles| Field | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Starts with a letter or digit; letters, digits, hyphens, underscores; max 64 characters. |
model | string | Yes | Any model id, max 128 characters. |
instructions | string | Yes | 1 to 20,000 characters. Markdown welcome — it reaches the model as written. |
Returns 201 with the profile object. A profile is enabled from the moment
it is created. The model is accepted as written and judged for
servability per request, so a profile may hold an id before it is available
— completions through the profile fail until it is, then work without a
profile edit.
curl https://api.greenpt.ai/v1/agent-profiles \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"slug": "support-triage",
"model": "gemma4",
"instructions": "Classify the message as bug, question, or feature request. Answer with the label, one sentence of reasoning, and a suggested reply."
}'Update a profile
PATCH /v1/agent-profiles/{id}Accepts any of slug, model, instructions, and enabled — at least
one. Returns 200 with the updated object. Note that changing slug
changes the profile's URL: clients addressing the old slug get a 404.
Delete a profile
DELETE /v1/agent-profiles/{id}Returns 204. The profile disappears from your list and its endpoint
answers 404, and the slug stays reserved to your account — creating
another profile under it answers 409. To retire a profile you might want
back, set enabled to false instead; a disabled profile stays listed and
editable while its endpoint answers 404.
Errors
Error responses on profile routes are plain text, not JSON.
| Status | When | Body |
|---|---|---|
400 | The body's model contradicts the pinned one | The agent profile pins the model; omit "model", send "", or send "<model>". |
400 | A create or update body fails validation | Invalid agent profile: followed by one line per issue. |
401 | Missing or invalid API key | Unauthorized |
404 | The slug does not resolve to an enabled profile of yours | Unknown agent profile "<slug>". |
404 | The id does not exist or is not yours | Agent profile not found |
409 | The slug is already taken among your profiles | You already have an agent profile named "<slug>". |
503 | Mangrove profiles are not switched on in this environment | Agent profiles are not enabled |
A 404 is deliberately uninformative: a disabled, deleted, or never-created
slug all answer the same way, so a slug never reveals whether it exists in a
state you cannot use. The 503 is answered before authentication; on
completions, every other error follows authentication, credit, and
rate-limit checks, so their status codes take precedence.
Limits
| What | Limit |
|---|---|
| Profiles per request | 1 — the URL names it |
| Slug | 64 characters |
| Model id | 128 characters |
| Instructions | 20,000 characters |