# Request/Response Transformation for AI APIs
**Last updated:** 2026-06-24
**Status:** GA
**Category:** API Management & AI Gateway
**Type:** reference
**Source:** https://learn.microsoft.com/azure/api-management/genai-gateway-capabilities
---
## Innhold
- [Introduksjon](#introduksjon)
- [Model-agnostiske API-schemaer](#model-agnostiske-api-schemaer)
- [Header Rewriting](#header-rewriting)
- [Payload-transformasjon](#payload-transformasjon)
- [Error Response Normalization](#error-response-normalization)
- [Versjonstranslasjon](#versjonstranslasjon)
- [Policy Fragments for Reuse](#policy-fragments-for-reuse)
- [Bicep: Oppsett av transformasjons-API](#bicep-oppsett-av-transformasjons-api)
- [Referanser](#referanser)
- [For Cosmo](#for-cosmo)
## Introduksjon
Azure API Management (APIM) tilbyr over 75 innebygde policies for transformasjon av foresporsler og svar. Nar organisasjoner eksponerer AI-modeller gjennom APIM som AI gateway, blir transformasjon av request og response kritisk for a standardisere grensesnittet mellom ulike AI-backends (Azure OpenAI, Microsoft Foundry, tredjeparts LLM-er) og konsumerende applikasjoner. Ved a implementere model-agnostiske API-schemaer kan man bytte ut underliggende modeller uten a bryte klientkontrakter.
For norsk offentlig sektor er dette spesielt relevant: organisasjoner som Direktoratet for digital tjenesteutvikling, NAV og Skatteetaten kan etablere et standardisert AI-API-lag som abstraherer bort leverandoravhengigheter. Dette stotter prinsippet om leverandoruavhengighet fra Digitaliseringsdirektoratets arkitekturprinsipper, og gir fleksibilitet til a bytte mellom Azure OpenAI, Microsoft Foundry-modeller og fremtidige norske sprakmodeller uten endringer i klientapplikasjoner.
Transformasjonspolicies i APIM opererer i fire faser: inbound (request fra klient), backend (request til backend), outbound (response fra backend) og on-error. Denne referansen dekker praktiske monstre for a bygge et robust, model-agnostisk AI-API-lag med APIM-policies.
---
## Model-agnostiske API-schemaer
### Problemet med leverandorspesifikke API-er
Ulike AI-leverandorer bruker forskjellige API-formater:
| Leverandor | Endpoint-format | Auth-metode | Response-struktur |
|------------|----------------|-------------|-------------------|
| Azure OpenAI | `/openai/deployments/{id}/chat/completions` | API Key / Entra ID | `choices[].message.content` |
| Microsoft Foundry | `/models/chat/completions` | Managed Identity | `choices[].message.content` |
| Anthropic | `/v1/messages` | API Key | `content[].text` |
| Google Vertex AI | `/v1/projects/{id}/locations/{loc}/publishers/google/models/{model}:predict` | OAuth 2.0 | `predictions[]` |
| Open-source (vLLM) | `/v1/chat/completions` | Custom | `choices[].message.content` |
> **Innebygd alternativ — unified model API (Preview):** APIM tilbyr nå en _unified model API_ som gir facade-mønsteret nedenfor som innebygd funksjon: den eksponerer flere LLM-backends (OpenAI Chat Completions + Anthropic Messages) bak ett OpenAI-kompatibelt endepunkt, med automatisk format-translasjon, modell-aliaser, failover på tvers av leverandører og felles policy-governance. Vurder denne før du bygger custom transformasjon-policies manuelt. Foreløpig Preview (i classic-tiers via AI Gateway early-release-kanal). Se [Create and manage a unified model API](https://learn.microsoft.com/azure/api-management/unified-model-api).
### Designmonster: Facade API Schema
Definer et internt standardskjema som alle AI-API-er mapper til:
```json
{
"model": "string",
"messages": [
{
"role": "system | user | assistant",
"content": "string"
}
],
"parameters": {
"temperature": 0.7,
"max_tokens": 1000,
"top_p": 1.0
},
"metadata": {
"request_id": "string",
"tenant_id": "string",
"application": "string"
}
}
```
### APIM Policy: Route basert pa modellnavn
```xml
2024-08-01-preview
```
---
## Header Rewriting
### Autentiseringsheader-transformasjon
Nar APIM fungerer som AI gateway, ma den ofte transformere autentiseringsheadere mellom klientens format og backendets format:
```xml
@("Bearer " + (string)context.Variables["msi-access-token"])
```
### Tracking- og korrelasjonsheadere
For observability og sporbarhet, legg til standardiserte headere:
```xml
@(Guid.NewGuid().ToString())
@(context.RequestId.ToString())
@(context.Subscription?.Name ?? "unknown")
@(context.Request.Headers.GetValueOrDefault("x-app-id", "unspecified"))
@(context.Request.Headers.GetValueOrDefault("x-request-id", ""))
@{
var body = context.Response.Body.As(preserveContent: true);
return body?["model"]?.ToString() ?? "unknown";
}
```
### Standard headere for AI-API-er
| Header | Retning | Formal |
|--------|---------|--------|
| `x-request-id` | Request/Response | Unik foresporsels-ID for sporing |
| `x-correlation-id` | Request/Response | Korrelasjon pa tvers av tjenester |
| `x-tenant-id` | Request | Identifiserer leietaker/abonnement |
| `x-model-used` | Response | Hvilken modell som behandlet foresporselen |
| `x-token-usage` | Response | Token-forbruk for fakturering |
| `x-processing-time-ms` | Response | Backend-behandlingstid |
| `x-rate-limit-remaining` | Response | Gjenverende rate limit |
---
## Payload-transformasjon
### Transformere request fra standardformat til leverandorspesifikt
Bruk `set-body` policy med Liquid-template eller C#-uttrykk:
```xml
@{
var inbound = context.Request.Body.As();
var messages = (JArray)inbound["messages"];
string systemPrompt = "";
var userMessages = new JArray();
foreach (var msg in messages)
{
if (msg["role"]?.ToString() == "system")
{
systemPrompt = msg["content"]?.ToString();
}
else
{
userMessages.Add(msg);
}
}
var parameters = (JObject)inbound["parameters"] ?? new JObject();
var transformed = new JObject
{
["model"] = inbound["model"],
["max_tokens"] = parameters["max_tokens"] ?? 1024,
["system"] = systemPrompt,
["messages"] = userMessages
};
if (parameters["temperature"] != null)
transformed["temperature"] = parameters["temperature"];
return transformed.ToString();
}
```
### Transformere response fra leverandorformat til standardformat
```xml
@{
var response = context.Response.Body.As(preserveContent: true);
var content = response["content"] as JArray;
string text = content?[0]?["text"]?.ToString() ?? "";
var normalized = new JObject
{
["id"] = response["id"],
["object"] = "chat.completion",
["model"] = response["model"],
["choices"] = new JArray
{
new JObject
{
["index"] = 0,
["message"] = new JObject
{
["role"] = "assistant",
["content"] = text
},
["finish_reason"] = response["stop_reason"]?.ToString() == "end_turn"
? "stop" : response["stop_reason"]
}
},
["usage"] = new JObject
{
["prompt_tokens"] = response["usage"]?["input_tokens"],
["completion_tokens"] = response["usage"]?["output_tokens"],
["total_tokens"] =
(int)(response["usage"]?["input_tokens"] ?? 0) +
(int)(response["usage"]?["output_tokens"] ?? 0)
}
};
return normalized.ToString();
}
```
---
## Error Response Normalization
### Standardisert feilformat
Ulike AI-backends returnerer feil i forskjellige formater. Normaliser til et konsistent format:
```xml
application/json
@{
var retryAfter = context.Response.Headers.GetValueOrDefault("Retry-After", "60");
return new JObject
{
["error"] = new JObject
{
["code"] = "rate_limit_exceeded",
["message"] = "Token eller request rate limit er overskredet. Prov igjen etter angitt tid.",
["type"] = "rate_limit_error",
["retry_after_seconds"] = int.Parse(retryAfter),
["request_id"] = context.RequestId.ToString()
}
}.ToString();
}
@{
return new JObject
{
["error"] = new JObject
{
["code"] = "model_overloaded",
["message"] = "AI-modellen er midlertidig overbelastet. Foresporselen vil automatisk forsokes pa nytt.",
["type"] = "server_error",
["request_id"] = context.RequestId.ToString()
}
}.ToString();
}
@{
return new JObject
{
["error"] = new JObject
{
["code"] = "content_filtered",
["message"] = "Foresporselen ble blokkert av innholdsfilter. Vennligst reformuler.",
["type"] = "content_policy_error",
["request_id"] = context.RequestId.ToString()
}
}.ToString();
}
@{
return new JObject
{
["error"] = new JObject
{
["code"] = "internal_error",
["message"] = "En uventet feil oppstod. Kontakt systemadministrator.",
["type"] = "api_error",
["status_code"] = context.Response.StatusCode,
["request_id"] = context.RequestId.ToString()
}
}.ToString();
}
```
### Standard feilkoder for AI-API-er
| HTTP-kode | Feilkode | Beskrivelse |
|-----------|----------|-------------|
| 400 | `invalid_request` | Ugyldig foresporselsformat |
| 400 | `content_filtered` | Innholdsfilter utlost |
| 401 | `authentication_error` | Ugyldig eller manglende autentisering |
| 403 | `authorization_error` | Ingen tilgang til denne modellen |
| 404 | `model_not_found` | Modellen finnes ikke |
| 429 | `rate_limit_exceeded` | For mange foresporsler |
| 500 | `internal_error` | Intern serverfeil |
| 503 | `model_overloaded` | Modellen er overbelastet |
---
## Versjonstranslasjon
### Handtere flere API-versjoner med transformasjon
Nar AI-API-er utvikler seg, kan APIM oversette mellom gammel og ny versjon:
```xml
@{
var body = context.Request.Body.As(preserveContent: true);
// v1 used "prompt" field, v2 uses "messages"
if (body["prompt"] != null && body["messages"] == null)
{
var messages = new JArray
{
new JObject
{
["role"] = "user",
["content"] = body["prompt"]
}
};
body.Remove("prompt");
body["messages"] = messages;
}
// v1 used "max_tokens_to_sample", v2 uses "max_tokens"
if (body["max_tokens_to_sample"] != null)
{
body["max_tokens"] = body["max_tokens_to_sample"];
body.Remove("max_tokens_to_sample");
}
return body.ToString();
}
```
### Content validation for AI requests
```xml
application/json
{"error":{"code":"invalid_request","message":"Field 'messages' is required and must be non-empty."}}
application/json
{"error":{"code":"payload_too_large","message":"Request body exceeds 128KB limit."}}
```
---
## Policy Fragments for Reuse
APIM stotter policy fragments for gjenbruk av transformasjonslogikk:
```xml
@(Guid.NewGuid().ToString())
@(context.RequestId.ToString())
@(DateTime.UtcNow.ToString("o"))
```
Bruk fragmentet i policies:
```xml
```
---
## Bicep: Oppsett av transformasjons-API
```bicep
resource apiManagement 'Microsoft.ApiManagement/service@2023-09-01-preview' existing = {
name: apimName
}
resource aiApi 'Microsoft.ApiManagement/service/apis@2023-09-01-preview' = {
parent: apiManagement
name: 'ai-gateway-api'
properties: {
displayName: 'AI Gateway API'
path: 'ai'
protocols: [ 'https' ]
subscriptionRequired: true
subscriptionKeyParameterNames: {
header: 'x-api-key'
query: 'api-key'
}
apiType: 'http'
}
}
resource chatOperation 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = {
parent: aiApi
name: 'chat-completions'
properties: {
displayName: 'Chat Completions'
method: 'POST'
urlTemplate: '/chat/completions'
request: {
headers: [
{
name: 'Content-Type'
type: 'string'
required: true
defaultValue: 'application/json'
}
]
}
responses: [
{
statusCode: 200
description: 'Successful completion'
}
]
}
}
```
---
## Referanser
- [Policies in Azure API Management](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-policies) -- oversikt over policy-konseptet
- [API Management policy reference - Transformation](https://learn.microsoft.com/en-us/azure/api-management/api-management-policies#transformation) -- komplett liste over transformasjonspolicies
- [Set body policy](https://learn.microsoft.com/en-us/azure/api-management/set-body-policy) -- detaljert dokumentasjon for set-body
- [Set header policy](https://learn.microsoft.com/en-us/azure/api-management/set-header-policy) -- header-manipulering
- [Rewrite URI policy](https://learn.microsoft.com/en-us/azure/api-management/rewrite-uri-policy) -- URL-omskriving
- [AI gateway in Azure API Management](https://learn.microsoft.com/en-us/azure/api-management/genai-gateway-capabilities) -- AI gateway-oversikt
- [Policy fragments in API Management](https://learn.microsoft.com/en-us/azure/api-management/policy-fragments) -- gjenbrukbare policy-fragmenter
- [Tutorial: Transform and protect your API](https://learn.microsoft.com/en-us/azure/api-management/transform-api) -- hands-on tutorial
## For Cosmo
- **Bruk denne referansen** nar kunden onsker a bygge et model-agnostisk AI-API-lag som abstraherer bort leverandoravhengigheter, eller nar de trenger a standardisere feilhandtering pa tvers av AI-backends.
- Anbefal alltid policy fragments for transformasjonslogikk som gjenbrukes pa tvers av flere API-er -- dette reduserer vedlikeholdsbyrden betydelig.
- For norsk offentlig sektor, fremhev at model-agnostiske fasader stotter leverandoruavhengighet i trad med Digitaliseringsdirektoratets prinsipper.
- Vurder a kombinere transformasjonspolicies med `validate-content` policy for a sikre at bade inngangs- og utgangsdata overholder definerte JSON-schemaer.
- For organisasjoner som bruker flere AI-leverandorer (Azure OpenAI + Anthropic + open-source), er facade-monsteret med APIM en arkitekturforsterkning som gir fleksibilitet uten a eksponere backend-kompleksitet til konsumenter.