# SDKs and language examples

Use a standard HTTP client, or configure a tested Chat Completions client with the Synderesis base URL and key. These examples call the documented customer endpoints from a server.

## Compatibility matrix

| Feature | Transcript/tool chat | Answer-adapter chat |
| --- | --- | --- |
| Preferred selector | synderesis-spark; pro or synderesis-pro with tools=[] for a Pro transcript | spark, pro, or an omitted model for automatic selection |
| Full supplied transcript | Yes, within message/body bounds | Last user message plus system instructions |
| Developer-role messages | Accepted | Not forwarded as developer instructions |
| Text content parts | Accepted and joined as text | Do not use; send string content |
| Function tools | Supported for the documented aliases | Presence selects the tool path for spark, pro and synderesis-pro; automatic selection rejects them with 400 unsupported_parameter |
| max_completion_tokens | Honored; takes precedence | Not used |
| max_tokens | Honored when max_completion_tokens is absent | Honored/clamped |
| stream=true | Buffered SSE after generation | 400 unsupported_parameter |
| stream_options.include_usage | Adds a usage chunk when exactly true | No streaming support |
| n | Only 1 | Not a multi-completion interface |
| response_format | Omitted/null only | Not forwarded as structured-output control |
| temperature, top_p, seed, penalties | Not forwarded by this handler | Not forwarded as sampling controls |
| store, metadata, user | Do not create persistence/metadata features | Do not create persistence/metadata features |
| Responses/Assistants/embeddings/audio/files APIs | Outside this documented surface | Outside this documented surface |

Supported format does not mean complete compatibility with all OpenAI endpoints, model features or SDK methods. Explicitly disable or review SDK retries, because ambiguous failures may already have consumed work.

## Client versions checked

**OpenAI Python 3.46.0** and **OpenAI JavaScript/TypeScript 7.53.0** were checked for Chat Completions creation, buffered SSE with usage, the Pro transcript selector and typed 400 error parsing.

These checks used the real Synderesis HTTP handler with synthetic identity, quota and generation dependencies. They verify client serialization/parsing against that contract; they are not a live model-quality test or a guarantee for untested SDK versions/features.

## Python: standard library HTTP

Save as example.py. Python 3.12 is sufficient; this sample has no third-party client dependency.

```python
import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen

key = os.environ["SYNDERESIS_API_KEY"]
body = {
    "model": "spark",
    "question": "Explain human dignity in two sentences.",
    "grounding": True,
    "max_tokens": 1024,
}
request = Request(
    "https://www.synderesis.eu/v1/answers",
    data=json.dumps(body).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {key}",
        "Content-Type": "application/json",
    },
    method="POST",
)
try:
    with urlopen(request, timeout=60) as response:
        payload = json.load(response)
        print(payload["answer"])
        print(payload["usage"])
except HTTPError as error:
    payload = json.loads(error.read().decode("utf-8"))
    detail = payload.get("error", {})
    raise RuntimeError(
        f"Synderesis HTTP {error.code}: {detail.get('code')}; "
        f"request_id={detail.get('request_id')}"
    ) from None
```

Run with python example.py after injecting the environment variable.

## Python: Chat Completions SDK

Install the checked version:

```bash
python -m pip install openai==3.46.0
```

```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["SYNDERESIS_API_KEY"],
    base_url="https://www.synderesis.eu/v1",
    timeout=120.0,
    max_retries=0,
)
completion = client.chat.completions.create(
    model="synderesis-spark",
    messages=[{"role": "user", "content": "Explain human dignity briefly."}],
    max_completion_tokens=1024,
)
message = completion.choices[0].message
print(message.content or "")
print(completion.usage)
```

A tool-call message can have null content; inspect tool_calls and authorise/execute functions in your application. See [the tool loop](streaming-and-tools.md#the-complete-tool-loop).

For a stream, create the request with stream=True and stream_options={"include_usage": True}; iterate the returned chunks and handle empty choices in the usage chunk.

## JavaScript: Node.js fetch

Save as example.mjs and run node example.mjs on Node.js 20 or later. This is server-side code.

```javascript
const key = process.env.SYNDERESIS_API_KEY;
if (!key) throw new Error("Set SYNDERESIS_API_KEY in this server process.");

const response = await fetch(
  "https://www.synderesis.eu/v1/chat/completions",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${key}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "synderesis-spark",
      messages: [{ role: "user", content: "Explain human dignity briefly." }],
      max_completion_tokens: 1024,
    }),
    signal: AbortSignal.timeout(120_000),
  },
);
const payload = await response.json();
if (!response.ok) {
  throw new Error(
    `Synderesis HTTP ${response.status}: ${payload.error?.code}; request_id=${payload.error?.request_id}`,
  );
}
console.log(payload.choices[0].message.content ?? "");
console.log(payload.usage);
```

Do not put this code and a real key in a web page. Call your own authenticated backend from the browser.

## JavaScript/TypeScript: Chat Completions SDK

Install:

```bash
npm install openai@7.53.0
```

```javascript
import OpenAI from "openai";

const key = process.env.SYNDERESIS_API_KEY;
if (!key) throw new Error("Set SYNDERESIS_API_KEY.");

const client = new OpenAI({
  apiKey: key,
  baseURL: "https://www.synderesis.eu/v1",
  timeout: 120_000,
  maxRetries: 0,
});
const stream = await client.chat.completions.create({
  model: "synderesis-spark",
  messages: [{ role: "user", content: "Explain human dignity briefly." }],
  max_completion_tokens: 1024,
  stream: true,
  stream_options: { include_usage: true },
});
for await (const chunk of stream) {
  const text = chunk.choices[0]?.delta?.content;
  if (text) process.stdout.write(text);
  if (chunk.usage) console.error(JSON.stringify(chunk.usage));
}
process.stdout.write("\n");
```

The server buffers this stream until generation completes. For a non-streaming response, omit stream and stream_options and read completion.choices[0].message.

## PowerShell

The environment variable is read in the current process. ConvertTo-Json produces the request body without shell-specific JSON quoting.

```powershell
if (-not $env:SYNDERESIS_API_KEY) {
    throw "Set SYNDERESIS_API_KEY in this process."
}
$synderesisBody = @{
    model = "spark"
    question = "Explain human dignity in two sentences."
    grounding = $true
    max_tokens = 1024
} | ConvertTo-Json -Depth 8

$synderesisRequest = @{
    Method = "Post"
    Uri = "https://www.synderesis.eu/v1/answers"
    Headers = @{ Authorization = "Bearer $env:SYNDERESIS_API_KEY" }
    ContentType = "application/json; charset=utf-8"
    Body = [Text.Encoding]::UTF8.GetBytes($synderesisBody)
    TimeoutSec = 120
}
$synderesisResult = Invoke-RestMethod @synderesisRequest
$synderesisResult.answer
$synderesisResult.usage
```

## C#: .NET 8 or later

Create a console project and replace Program.cs with this complete HTTP example.

```csharp
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

string key = Environment.GetEnvironmentVariable("SYNDERESIS_API_KEY")
    ?? throw new InvalidOperationException("Set SYNDERESIS_API_KEY.");

using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(120) };
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", key);

using var response = await client.PostAsJsonAsync(
    "https://www.synderesis.eu/v1/answers",
    new {
        model = "spark",
        question = "Explain human dignity in two sentences.",
        grounding = true,
        max_tokens = 1024
    }
);
string text = await response.Content.ReadAsStringAsync();
using var payload = JsonDocument.Parse(text);
if (!response.IsSuccessStatusCode)
{
    var error = payload.RootElement.GetProperty("error");
    throw new InvalidOperationException(
        $"Synderesis HTTP {(int)response.StatusCode}; " +
        $"code={error.GetProperty("code").GetString()}; " +
        $"request_id={error.GetProperty("request_id").GetString()}"
    );
}
Console.WriteLine(payload.RootElement.GetProperty("answer").GetString());
```

## Java: Java 21 or later

Save as Example.java. The built-in HTTP client sends the same JSON contract; the example prints the JSON response so your application's JSON library can parse answer and usage.

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Example {
    public static void main(String[] args) throws Exception {
        String key = System.getenv("SYNDERESIS_API_KEY");
        if (key == null || key.isBlank()) {
            throw new IllegalStateException("Set SYNDERESIS_API_KEY.");
        }
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(15)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://www.synderesis.eu/v1/answers"))
            .timeout(Duration.ofSeconds(120))
            .header("Authorization", "Bearer " + key)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString("""
                {"model":"spark","question":"Explain human dignity briefly.",
                 "grounding":true,"max_tokens":1024}
                """))
            .build();
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString()
        );
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException(
                "Synderesis HTTP " + response.statusCode() +
                "; request_id=" +
                response.headers().firstValue("x-request-id").orElse("unavailable")
            );
        }
        System.out.println(response.body());
    }
}
```

Run with java Example.java. Production applications should parse the response using their chosen JSON library and keep error bodies redacted.

## Environments and deployment

Use the server runtime's secret manager for development, CI, containers and cloud deployments. A browser framework's public environment-variable prefix must never be used for a customer key.

For serverless functions, explicitly account for cold starts and platform timeouts. For containers, inject secrets at runtime rather than baking them into image layers. Test the exact runtime, client version and proxy combination you deploy.

The [production guide](production.md) covers retries, uncertainty, concurrency and monitoring. [Migration guidance](migration-and-faq.md) explains differences from older examples and other API providers.
