Skip to main content

What it is

This guide is for anyone who’s going to build something on top of Symphony: a script, a backend, an automation, an internal assistant, an agent that runs on its own. It assumes you know how to code but don’t have access to Symphony’s code. Everything here can be done with just your API key and an HTTP client. If you only want the quick steps to generate the key and make the first call, start with Connect via API and come back here when you’re building the real project.
The core idea: Symphony’s chat API speaks the same protocol as OpenAI. If your project already uses the openai library (Python, Node, LangChain, LlamaIndex, Vercel AI SDK…), you change the URL and the key and the rest of the code stays the same. The difference is that you now use your company’s models, agents, and knowledge bases, with usage controlled by your organization.

Prerequisites

  • An active AI Symphony account.
  • An API key (starts with sk-).
  • Your company ID, for the X-Enterprise-Id header.
  • Any HTTP client (cURL, requests, fetch, Postman) or an OpenAI-compatible library.

1. Setting up access

1.1 Generate your API key

1

Click your username

Open Symphony and click your name in the bottom-left corner.
2

Open API keys

Go to Settings → Account tab (or Connections, depending on the version) → API keys.
3

Generate and copy the key

Click to generate the key and copy the full value. It starts with sk-.
You only have one key. Generating a new one replaces the previous one: every integration that used the old key stops working right away. Before you generate again, make sure no project in production depends on the current key.

1.2 Find your company ID

Symphony serves several companies, and each has its own catalog of models, agents, and knowledge bases. Since the API key doesn’t carry this information, you need to say which company you’re working in, with the header:
Ask your company’s Symphony administrator for this ID. They can find it in the admin panel.
This is the number 1 mistake for newcomers: without this header, you get “model not found” for a model you can see working in the interface. The system doesn’t know which company you mean and falls back to the environment’s default catalog, which is probably not yours.

1.3 The three headers you’ll always use

That’s it. You don’t need cookies or a CSRF token. Those steps appear in documents describing how the browser talks to Symphony. Integrations that use an API key skip them. Base URL: https://symphony.fcamara.com

2. Your first call

The response comes in the classic OpenAI format:
Only model and messages are required. If you saw somewhere that you need to send id, chat_id, and session_id, that describes what the Symphony interface sends, not an API requirement. You only need those fields if you want the conversation to show up in the platform’s history (see section 8. Keeping conversation history).

What else you can send

Not every model accepts every parameter. Some reject stop, others only accept the default temperature. When that happens, Symphony returns a message explaining exactly which parameter was the problem.

3. Finding the models and agents you can use

Never guess the model value. Identifiers are defined by your company and change from environment to environment.
Three important things about this list:
  • It’s already filtered to what your account can use. Disabled models, agents nobody shared with you, and models outside the company catalog simply don’t appear. If something is in your interface but not here, the problem is almost always X-Enterprise-Id.
  • Agents appear alongside models. Every item with info.base_model_id is an agent created on the platform. Using an agent is the API’s most powerful shortcut: the system prompt, knowledge bases, and tools configured in it are applied automatically, without you sending anything beyond model.
  • Copy the id exactly as returned. Using the model name from the original provider (gemini-2.5-pro) instead of the Symphony identifier (FC.gemini-2.5-pro) is the most common cause of 404 errors.

4. Connecting your project with a ready-made library

Since the API is OpenAI-compatible, you don’t need to write an HTTP client by hand. Point base_url to https://symphony.fcamara.com/api: the library appends /chat/completions on its own.

In VS Code / JetBrains

If your “project” is coding with AI assistance in the editor, you don’t need code: see IDE Plugin.

5. Project recipes

5.1 Use a company agent as your product’s engine

This is the most underrated recipe. Instead of keeping a giant prompt inside your code, create an agent in the Symphony interface, with a system prompt, knowledge base, and tools, and call it by id:
Why it’s worth it: the business person or specialist becomes the one who tunes the behavior, directly in the interface, without redeploying your system. Your code is only responsible for the integration.

5.2 Assistant with conversation memory

The API is stateless by default: each call is independent. To keep context, send the history:
History grows and cost grows with it: you pay tokens for everything you resend. In long conversations, trim the oldest messages, or ask the model for a periodic summary of what was discussed and replace the old part with that summary. If you exceed the model’s window, you get a context limit error.There’s an alternative where Symphony keeps the history for you. See section 8. Keeping conversation history.

5.3 Structured data extraction

When you need reliable JSON instead of text, use response_format:
Not every model supports this feature. Those that don’t return free text. Always validate the JSON on your side before trusting it.

5.4 Questions and answers about company documents (RAG)

Create the knowledge base in the interface (it’s simpler) and then use its id in your calls. See section 7. Working with documents and knowledge bases.

5.5 Let the model call your system’s functions

The function calling pattern works the same as OpenAI’s: you describe the functions, the model returns tool_calls, your code executes them and returns the result as a message with the tool role.
Don’t confuse these with the Tools registered in Symphony (Tools): the platform runs those on its own, and you get them for free when you use an agent that has them configured. The tools in this recipe are functions of your system, which only your code knows how to run.

5.6 Batch processing

To classify, summarize, or translate many items:
  • Limit concurrency. Start with 3 to 5 requests in parallel and increase slowly, watching for 429 errors.
  • Save progress after each item, so you can resume where you left off.
  • Retry only on the right errors (see section 9. Errors).
  • Track usage at GET /api/v1/consumption/me. A large batch can use up your monthly limit before it finishes.

6. Real-time responses (streaming)

With "stream": true, the response arrives in chunks as it’s generated. That’s what gives chat interfaces the “typing” feel.
If you read the stream by hand, the format is a sequence of data: {...} lines, ending with data: [DONE].
Three traps that catch almost everyone:
  1. An error can arrive after the 200 OK. The status code is sent before the response starts. If the model fails mid-generation, the failure comes as an event inside the stream, not as an HTTP error. Handle it.
  2. Don’t count on one chunk per token. Events can arrive grouped or split by the network. Buffer by line before parsing.
  3. [DONE] is a signal, not content. Ignore it and ignore blank lines.

7. Working with documents and knowledge bases

7.1 Send a document along with the question

1

Upload the file

2

Wait for processing to finish

The upload responds right away, but reading the file (text extraction, OCR) keeps running in the background. Poll until the status leaves processing:
3

Attach the file to the question

Large or scanned PDFs can take minutes. If you attach the file while it’s still processing, the model responds as if the document were empty. That’s a silent bug that’s hard to find later.
Documents attached this way go into the context in full. That’s the right behavior when the file is the subject of the conversation.

7.2 Use a knowledge base

Knowledge bases are for the opposite case: lots of content, of which only the relevant parts should go into each question. Create the base in the interface (Knowledge base), copy its id, and use:
Symphony looks up the relevant parts and injects them into the context automatically. To list the bases you can see, use GET /api/v1/knowledge/list/compact (see the API reference).
A large base gives partial answers to broad questions. When the base doesn’t fit in the model’s window, only the most relevant parts go in. Questions like “summarize everything” or “how many documents mention X” tend to come out incomplete. Prefer specific questions, or a model with a larger context window.
If you use an agent that already has a base configured, you don’t need to send files: it brings the base along.

8. Keeping conversation history

By default nothing is saved: you send the history on each call and Symphony keeps nothing. If you want your project’s conversations to show up in the platform’s history (for auditing, so the user can continue the conversation in the interface, or just so you don’t have to manage history), create a chat and reuse the chat_id:
Then, on the following calls:
The golden detail: if you send the chat_id and only the user’s new question (no assistant role message in the array), Symphony retrieves the saved history and puts it before your message. In other words: you can keep a long conversation going by sending only the new question. Two conditions for this to work:
  • If your array already has an assistant message, Symphony assumes you’re managing the history and injects nothing.
  • In a new conversation, with no saved response yet, there’s nothing to inject either.

9. Errors: how to interpret them and what to do

Error responses include a stable code in addition to the text. Depending on the endpoint, the code comes in error.code or in detail.code:
The possible formats and sample code to read both are in Errors and limits.
Code against the code, never the message text. The text changes with language and wording improvements; the code doesn’t. And store the request_id in your logs: that’s how the Symphony team can trace what happened when you open a ticket.

What each status code means here

Codes you’ll see most often

Retry policy

  • Retry on 429 and 5xx, with exponential backoff and a bit of randomness, respecting the Retry-After header when present.
  • Never retry 400, 401, 402, 403, 404, and 409. None of them resolves on its own, and insisting only burns your request quota.

10. Usage limits

There are three different limits, and mixing them up leads to pointless retries:
A cap on requests per minute, counted per IP address. Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset to guide you. Since the limit is per IP, several integrations leaving from the same network share the same quota. Wait and try again.
Token quota contracted by your organization. The body tells you when it renews. Retrying only helps after that.
Your user’s monthly spending cap, or an administrative block. It’s checked before any call to the model, so nothing is spent. Check it anytime:
Retrying never resolves a 402. Only an administrator raising your limit, or the start of the next month.
Response time: set generous timeouts. Large models, with lots of context or attached files, can take minutes. A short timeout on your client will cut off legitimate responses.

11. Best practices

Do

  • Store the key in an environment variable or secrets vault.
  • Always use HTTPS.
  • Ask the administrator for a dedicated technical account for each integration. This prevents regenerating your personal key from breaking the automation, and keeps the project’s usage separate from your individual usage in company reports.
  • Set X-Enterprise-Id through configuration, never inferred at runtime.
  • Call GET /api/models once at startup and check that the model you expect exists. Failing at startup is much better than failing in the middle of a batch.
  • Log the request_id of every error response.
  • Measure usage before scaling volume.

Don't

  • Don’t put the key in code that runs in the browser. It gives full access to your account, with no origin or scope restriction. If your project has a front end, the key stays in your backend, which makes the call.
  • Don’t commit the key to a repository, or put it in a URL, log, or query parameter.
  • Don’t retry requests that failed with a deterministic 4xx error.
  • Don’t treat the file upload’s 200 OK as “ready to use”.
  • Don’t assume a model that works today will exist tomorrow: the catalog is managed by your company.

12. Frequently asked questions

Yes, whenever the environment serves more than one company. Without it, Symphony doesn’t know which catalog you mean and you get “model not found” for models that exist.
No, when you use an API key without sending cookies. That step applies to anyone reproducing the browser flow with a session token.
No. But it’s replaced if you generate another one, and it can be revoked by the administrator.
Not today: it’s one key per user. The workaround is to request a separate technical account for each integration.
No. It’s worth exactly what your account is worth: same models, same agents, same bases, same usage limit. There’s no “read-only” key or key restricted to one agent.
One of the id values returned by GET /api/models. If there’s a company agent for your use case, prefer the agent: it already comes with prompt, base, and tools set up.
Yes, any that are compatible with the OpenAI protocol: the openai SDK, LangChain, LlamaIndex, Vercel AI SDK, Continue.dev, and the like.
GET /api/v1/consumption/me. Full reports are available to the company administrator.
Not in production. All available endpoints are described in the API reference.
Check that the header is exactly Authorization: Bearer sk-..., with no quotes, no extra space, and the complete key. Also check that nobody generated a new key on the same account.
The file was probably still being processed. Poll GET /api/v1/files/{id} until the status is ready.

13. Known limitations

  • A single key per user, with no name, no scope of its own, and no rotation without interruption. Generating a new one breaks the previous one immediately.
  • The key equals your entire account. There’s no service credential with reduced permissions.
  • The request limit is per IP, not per key. Integrations leaving from the same IP share the quota.
  • There’s no completion notification (webhook) for API consumers: integration is synchronous or by streaming.
  • There’s no official Symphony library. Use an OpenAI-compatible one.
  • There’s no Swagger in production.
  • The model catalog is managed by your company and can change without notice for integrators.
  • Some features depend on permissions (web search, image generation, code interpreter) and may be turned off for your account.

14. Checklist before going to production

  • Key in an environment variable or vault, never in code or the front end
  • Ideally, a dedicated technical account for this integration
  • X-Enterprise-Id coming from configuration, present on every call
  • Model validated at startup with GET /api/models
  • Error handling by code (in error.code or detail.code), not by text
  • Retry with backoff only on 429 and 5xx
  • request_id recorded in your error logs
  • Client timeout compatible with long responses
  • If streaming: [DONE], split events, and errors after the 200 handled
  • If using files: wait for ready status before attaching
  • If the conversation is long: a defined strategy so history doesn’t overflow the window
  • Usage monitored at GET /api/v1/consumption/me