This guide shows how to make direct API calls to SAP AI Core using curl for
testing and debugging. For production code, use the SAP AI SDK with
AICORE_SERVICE_KEY.
- Overview
- Prerequisites
- Step-by-Step Guide
- Request Body Structure (Orchestration v2)
- Tool Calling Example
- Complete Working Example
- Response Format
- Common Issues
- Debugging Tips
- Foundation Models API
- Security Best Practices
- Additional Resources
- TypeScript Examples
- Related Documentation
Complete OAuth2 authentication → API call → Tool calling flow.
- SAP AI Core instance + service key (from BTP cockpit) - see Environment Setup for credential configuration
curlandbase64utilities
Service key contains: clientid, clientsecret, url (auth server),
serviceurls.AI_API_URL
Your AICORE_SERVICE_KEY environment variable contains a JSON object. Here's how
to extract the required values:
Example AICORE_SERVICE_KEY structure:
{
"clientid": "sb-abc123!a12345|aicore!b123",
"clientsecret": "AbCdEf123456+ghIjKl==",
"url": "https://mysubaccount.authentication.eu10.hana.ondemand.com",
"serviceurls": {
"AI_API_URL": "https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com"
}
}Extract values using jq (recommended):
# Parse AICORE_SERVICE_KEY and extract individual values
CLIENT_ID=$(echo "$AICORE_SERVICE_KEY" | jq -r '.clientid')
CLIENT_SECRET=$(echo "$AICORE_SERVICE_KEY" | jq -r '.clientsecret')
AUTH_URL=$(echo "$AICORE_SERVICE_KEY" | jq -r '.url')
AI_API_URL=$(echo "$AICORE_SERVICE_KEY" | jq -r '.serviceurls.AI_API_URL')
# Verify extraction succeeded
echo "Client ID: ${CLIENT_ID:0:20}..." # Show first 20 chars only
echo "Auth URL: $AUTH_URL"
echo "API URL: $AI_API_URL"Alternative: Extract using grep/sed (no jq required):
# Extract values without jq (less robust but works in minimal environments)
CLIENT_ID=$(echo "$AICORE_SERVICE_KEY" | grep -o '"clientid":"[^"]*' | cut -d'"' -f4)
CLIENT_SECRET=$(echo "$AICORE_SERVICE_KEY" | grep -o '"clientsecret":"[^"]*' | cut -d'"' -f4)
AUTH_URL=$(echo "$AICORE_SERVICE_KEY" | grep -o '"url":"[^"]*' | cut -d'"' -f4)
AI_API_URL=$(echo "$AICORE_SERVICE_KEY" | grep -o '"AI_API_URL":"[^"]*' | cut -d'"' -f4)Tip: The
jqapproach is more reliable, especially when credentials contain special characters like+,=, or|. Install jq viaapt install jq,brew install jq, or your package manager.
#!/bin/bash
# Your credentials (replace with actual values)
CLIENT_ID="your-client-id-here"
CLIENT_SECRET="your-client-secret-here"
AUTH_URL="https://your-subdomain.authentication.region.hana.ondemand.com"
# Encode credentials to Base64
CREDENTIALS=$(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64 | tr -d '\n')
# Request OAuth token
TOKEN_RESPONSE=$(curl -s --request POST \
--url "${AUTH_URL}/oauth/token" \
--header "Authorization: Basic ${CREDENTIALS}" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=client_credentials")
# Extract access token from JSON response
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
# Verify token was obtained
if [ -z "$ACCESS_TOKEN" ]; then
echo "❌ Failed to get OAuth token"
echo "Response: $TOKEN_RESPONSE"
exit 1
fi
echo "✅ OAuth token obtained"Key Points: Tokens expire after 12h.
Endpoint:
https://{AI_API_URL}/v2/inference/deployments/{DEPLOYMENT_ID}/v2/completion
Note: The
/v2appears twice (base path + completion endpoint).
# Configuration
AI_API_URL="https://api.ai.prod.region.aws.ml.hana.ondemand.com"
DEPLOYMENT_ID="your-deployment-id"
RESOURCE_GROUP="default"
# Build endpoint URL
API_ENDPOINT="${AI_API_URL}/v2/inference/deployments/${DEPLOYMENT_ID}/v2/completion"
# Make API call
curl --request POST \
--url "${API_ENDPOINT}" \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "AI-Resource-Group: ${RESOURCE_GROUP}" \
--header "Content-Type: application/json" \
--data '{
"config": {
"modules": {
"prompt_templating": {
"prompt": {
"template": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "What is 2+2?"
}
]
},
"model": {
"name": "gpt-4.1",
"version": "latest"
}
}
}
}
}'{
"config": {
"modules": {
"prompt_templating": {
"prompt": {
/* Prompt configuration */
},
"model": {
/* Model configuration */
}
}
}
}
}The SAP AI Core v2 API uses a modular configuration structure with prompt
(messages, tools, response_format) and model (name, version, params) sections.
See complete working examples below for the full structure.
💡 For detailed parameter documentation, see API Reference
For complete tool calling documentation including all models, parallel execution, error handling, and best practices, see API Reference - Tool Calling.
For complete model capabilities and tool calling support, see API Reference - Model-Specific Tool Limitations.
#!/bin/bash
# ============================================
# Configuration (REPLACE WITH YOUR VALUES)
# ============================================
CLIENT_ID="your-client-id"
CLIENT_SECRET="your-client-secret"
AUTH_URL="https://your-auth-url.authentication.region.hana.ondemand.com"
AI_API_URL="https://api.ai.prod.region.aws.ml.hana.ondemand.com"
DEPLOYMENT_ID="your-deployment-id"
RESOURCE_GROUP="default"
# ============================================
# Get OAuth Token
# ============================================
echo "🔐 Getting OAuth token..."
CREDENTIALS=$(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64)
TOKEN_RESPONSE=$(curl -s --request POST \
--url "${AUTH_URL}/oauth/token" \
--header "Authorization: Basic ${CREDENTIALS}" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=client_credentials")
ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
if [ -z "$ACCESS_TOKEN" ]; then
echo "❌ Failed to get OAuth token"
exit 1
fi
echo "✅ OAuth token obtained"
# ============================================
# Call SAP AI Core API
# ============================================
echo "🚀 Calling SAP AI Core..."
API_ENDPOINT="${AI_API_URL}/v2/inference/deployments/${DEPLOYMENT_ID}/v2/completion"
curl --request POST \
--url "${API_ENDPOINT}" \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "AI-Resource-Group: ${RESOURCE_GROUP}" \
--header "Content-Type: application/json" \
--data '{
"config": {
"modules": {
"prompt_templating": {
"prompt": {
"template": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Calculate the total price: 100 per unit, 10 units."
}
],
"tools": [
{
"type": "function",
"function": {
"name": "calculate_total_price",
"description": "Calculate total price",
"parameters": {
"type": "object",
"properties": {
"price_per_unit": {
"type": "number",
"description": "Price per unit"
},
"quantity": {
"type": "number",
"description": "Number of units"
}
},
"required": ["price_per_unit", "quantity"],
"additionalProperties": false
}
}
}
]
},
"model": {
"name": "gpt-4.1",
"version": "latest"
}
}
}
}
}'
echo ""
echo "✅ Request completed"{
"request_id": "uuid",
"final_result": {
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1234567890,
"model": "gpt-4.1-2024-08-06",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The total price is $1000",
"tool_calls": [
{
"id": "call_xxx",
"type": "function",
"function": {
"name": "calculate_total_price",
"arguments": "{\"price_per_unit\": 100, \"quantity\": 10}"
}
}
]
},
"finish_reason": "tool_calls"
}
],
"usage": {
"completion_tokens": 59,
"prompt_tokens": 129,
"total_tokens": 188
}
}
}{
"error": {
"request_id": "uuid",
"code": 400,
"message": "Error description",
"location": "Module name"
}
}| Error | Cause | Solution |
|---|---|---|
| Missing Tenant Id | Expired token, wrong endpoint, missing AI-Resource-Group header, misconfigured service key |
Regenerate token, verify /v2 paths, ensure AI-Resource-Group header is set, check service key contains tenant information |
| Bad Credentials | Wrong client ID/secret, bad Base64 encoding | Check credentials, verify Base64 output has no newlines |
| Deployment Not Found | Wrong deployment ID, wrong region, wrong resource group | Verify deployment exists, check resource group |
| Multiple Tools Error | Gemini model with >1 tool | Use 1 tool OR switch to OpenAI/Claude models |
Verbose output:
curl --verbose --fail-with-body --show-error ...Decode JWT token:
echo "$ACCESS_TOKEN" | cut -d. -f2 | base64 -d | jq .Check: exp (expiration), subaccountid, scope
Minimal test request:
{
"config": {
"modules": {
"prompt_templating": {
"prompt": {
"template": [{ "role": "user", "content": "Hello" }]
},
"model": { "name": "gpt-4.1", "version": "latest" }
}
}
}
}The Foundation Models API provides direct model access with additional parameters
like logprobs, seed, and logit_bias. Use a different endpoint path.
${AI_API_URL}/v2/inference/deployments/${DEPLOYMENT_ID}/chat/completions
Note: Replace completion (Orchestration) with chat/completions (Foundation Models).
curl --request POST \
--url "${AI_API_URL}/v2/inference/deployments/${DEPLOYMENT_ID}/chat/completions" \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "AI-Resource-Group: ${RESOURCE_GROUP}" \
--header "Content-Type: application/json" \
--data '{
"model": "gpt-4.1",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"temperature": 0.7,
"max_tokens": 100
}'{
"model": "gpt-4.1",
"messages": [...],
"logprobs": true,
"top_logprobs": 5,
"seed": 42,
"logit_bias": {"50256": -100},
"user": "user-123"
}{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"model": "gpt-4.1",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop",
"logprobs": {
"content": [
{"token": "Hello", "logprob": -0.5, "top_logprobs": [...]}
]
}
}
],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 10,
"total_tokens": 30
}
}Note: The Foundation Models API uses standard OpenAI-compatible format, while the Orchestration API uses SAP's orchestration envelope format.
See Environment Setup - Security Best Practices for detailed guidance on credential management, key rotation, and secure deployment.
See examples/ directory: example-generate-text.ts,
example-streaming-chat.ts, example-chat-completion-tool.ts,
example-image-recognition.ts, example-data-masking.ts. More in
README.
For production, use the TypeScript provider package for better error handling and type safety.
- README — Quick start and usage guide
- API Reference — Complete API documentation
- Environment Setup — Authentication configuration
- Troubleshooting — Common issues and solutions