> ## Documentation Index
> Fetch the complete documentation index at: https://developers.octaviatech.app/llms.txt
> Use this file to discover all available pages before exploring further.

# SDKs

> Official client libraries for the Octavia AI CMS API.

Official client libraries are available for five languages. Every SDK wraps the same
REST endpoints, sends your API key automatically, and returns the same envelope shape
as the raw API.

<Note>
  <strong>New</strong> — the JavaScript SDK is the most complete and the one we
  build against first. Start there if you are unsure which to pick.
</Note>

<CardGroup cols={3}>
  <Card title="JavaScript" icon="js" href="/api-reference/ai-cms/sdks/javascript" arrow="true">
    Node.js 18+ and modern browsers. ESM and TypeScript types included.
  </Card>

  <Card title="Python" icon="file" href="/api-reference/ai-cms/sdks/python" arrow="true">
    Python 3.9+. No third-party dependencies.
  </Card>

  <Card title="PHP" icon="php" href="/api-reference/ai-cms/sdks/php" arrow="true">
    PHP 8.0+. PSR-18 compatible, Composer package.
  </Card>

  <Card title="C#" icon="circle-dashed" href="/api-reference/ai-cms/sdks/csharp" arrow="true">
    .NET 6+. Inject your own `HttpClient` for full control.
  </Card>

  <Card title="Go" href="/api-reference/ai-cms/sdks/go" arrow="true">
    Go 1.21+. Single module, zero dependencies.
  </Card>
</CardGroup>

***

## Install

<Tabs>
  <Tab title="JavaScript">`bash npm install @octaviatech/cms `</Tab>
  <Tab title="Python">`bash pip install octavia-cms-sdk `</Tab>
  <Tab title="PHP">`bash composer require octavia/cms-sdk `</Tab>
  <Tab title="C#">`bash dotnet add package Octavia.CmsSdk `</Tab>
  <Tab title="Go">`bash go get github.com/octaviatech/cms-sdk-go `</Tab>
</Tabs>

***

## Quick start

Every SDK is initialized with your API key. The base URL defaults to the production
endpoint, so the only required value is the key.

<Tabs>
  <Tab title="JavaScript">
    ```ts theme={null}
    import { CMS } from "@octaviatech/cms";

    const cms = CMS.init("sk_live_...");

    const res = await cms.article.getAll({ page: 1, limit: 10 });
    if (res.ok) {
      console.log(res.data);
    } else {
      console.error(res.error);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from octavia_cms_sdk import CMS

    cms = CMS.init("sk_live_...")

    res = cms.article.get_all({"page": 1, "limit": 10})
    if res.ok:
        print(res.data)
    else:
        print(res.error)
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    use Octavia\Cms\CMS;

    $cms = CMS::init("sk_live_...");

    $res = $cms->article->getAll(['page' => 1, 'limit' => 10]);
    if ($res->ok) {
        print_r($res->data);
    } else {
        print_r($res->error);
    }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    using Octavia.Cms;

    var cms = CMS.Init("sk_live_...");

    var res = await cms.Article.GetAllAsync(new Dictionary<string, object>
    {
        ["page"] = 1,
        ["limit"] = 10,
    });
    if (res.Ok) Console.WriteLine(res.Data);
    else Console.Error.WriteLine(res.Error?.Message);
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
        "fmt"

        "github.com/octaviatech/cms-sdk-go/sdk"
    )

    func main() {
        cms, err := sdk.InitCMS("sk_live_...", nil)
        if err != nil {
            panic(err)
        }

        res, err := cms.Article.GetAll(map[string]interface{}{"page": 1, "limit": 10})
        if err != nil {
            panic(err)
        }
        fmt.Println(res.Data)
    }
    ```
  </Tab>
</Tabs>

***

## Authentication

The only thing you need to configure is your API key. It is sent on every request as
the `x-api-key` header, and the SDKs do not expose any other header configuration —
there is nothing else for a caller to set.

<Note>
  Multi-tenancy headers are injected by the API gateway on your behalf. You
  never pass them, and no SDK option accepts them.
</Note>

Keep the key out of source control. Read it from the environment in every language:

<Tabs>
  <Tab title="JavaScript">
    ```ts theme={null}
    const cms = CMS.init(process.env.OCTAVIA_API_KEY!);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os

    cms = CMS.init(os.environ["OCTAVIA_API_KEY"])
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $cms = CMS::init(getenv('OCTAVIA_API_KEY'));
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    var cms = CMS.Init(Environment.GetEnvironmentVariable("OCTAVIA_API_KEY")!);
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    key := os.Getenv("OCTAVIA_API_KEY")
    cms, err := sdk.InitCMS(key, nil)
    ```
  </Tab>
</Tabs>

***

## Response shape

Every endpoint returns the same envelope, and every SDK unwraps it into one
consistent result object. See [Response format](/api-reference/ai-cms/response-fromat)
for the raw structure.

| Field   | Type           | Meaning                                                     |
| ------- | -------------- | ----------------------------------------------------------- |
| `ok`    | boolean        | `true` when the HTTP status was 2xx                         |
| `data`  | object         | The `data` field of the envelope, or `null`                 |
| `error` | object \| null | The API error, or `null` on success                         |
| `meta`  | object \| null | Pagination and count metadata, when the endpoint returns it |

By default an API error is a normal return value, not an exception. Set
`throw_on_error` to raise instead:

<Tabs>
  <Tab title="JavaScript">
    ```ts theme={null}
    const cms = CMS.init(process.env.OCTAVIA_API_KEY!, { throwOnError: true });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    cms = CMS.init(key, throw_on_error=True)
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $cms = CMS::init($key, ['throwOnError' => true]);
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    var cms = CMS.Init(key, new CMSOptions { ThrowOnError = true });
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    cms, err := sdk.InitCMS(key, &sdk.CMSOptions{ThrowOnError: true})
    ```
  </Tab>
</Tabs>

<Note>
  The Go SDK's `ThrowOnError` panics rather than returning an error, and the
  panic value is a plain string — recover with `defer`/`recover` if you enable
  it.
</Note>

***

## Resources

The names below are the same across all five SDKs. Method names differ only in
capitalisation convention.

| Resource         | Access            | Covers                                               |
| ---------------- | ----------------- | ---------------------------------------------------- |
| `article`        | `.article`        | Articles: CRUD, search, publish state, SEO analysis  |
| `author`         | `.author`         | Author profiles                                      |
| `category`       | `.category`       | Categories                                           |
| `subcategory`    | `.subcategory`    | Subcategories                                        |
| `form`           | `.form`           | Forms, captcha config                                |
| `formSubmission` | `.formSubmission` | Form submissions                                     |
| `language`       | `.language`       | Languages                                            |
| `tag`            | `.tag`            | Tags                                                 |
| `report`         | `.report`         | Statistics, dashboards, charts                       |
| `ai`             | `.ai`             | Summarize, translate, SEO, repurpose, social publish |
| `aiConversation` | `.aiConversation` | Chat sessions and messages                           |
| `raw`            | `.raw`            | The underlying HTTP client, for unlisted endpoints   |

The JS and Go SDKs are generated from the OpenAPI spec and cover the full surface,
including AI conversation, meta and tags. The Python, PHP and C# SDKs expose the
resources above; for anything outside them, call through `raw`.

***

## Configuration

<Tabs>
  <Tab title="JavaScript">
    ```ts theme={null}
    const cms = CMS.init(key, {
      baseUrl: "https://api.octaviatech.app/cms", // default
      timeoutMs: 30_000,                          // default
      throwOnError: false,                        // default
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    cms = CMS.init(key, timeout_ms=30_000, throw_on_error=False)
    ```
  </Tab>

  <Tab title="PHP">
    ```php theme={null}
    $cms = CMS::init($key, ['timeoutMs' => 0, 'throwOnError' => false]);
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    var cms = CMS.Init(key, new CMSOptions
    {
        BaseUrl = "https://api.octaviatech.app/cms",
        Timeout = TimeSpan.FromSeconds(30),
        ThrowOnError = false,
    });
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    client, err := sdk.NewClient(sdk.ClientConfig{
        BaseURL: sdk.CMSBaseURL,
        ApiKey:  key,
        Timeout: 30 * time.Second,
    })
    ```
  </Tab>
</Tabs>

<Warning>
  In the PHP SDK `timeoutMs` defaults to `0`, which means **no timeout**. Set it
  explicitly in production.
</Warning>

***

## Retry and rate limits

None of the SDKs retry automatically. A `429` is returned as a normal error result,
so you can inspect `retry-after` and back off yourself:

<Tabs>
  <Tab title="JavaScript">
    ```ts theme={null}
    const res = await cms.article.getAll();
    if (!res.ok && res.error?.status === 429) {
      const wait = Number(res.error.headers?.["retry-after"] ?? 1);
      await new Promise((r) => setTimeout(r, wait * 1000));
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import time

    res = cms.article.get_all()
    if not res.ok and res.error and res.error.status == 429:
        time.sleep(1)
    ```
  </Tab>
</Tabs>

<Note>
  `429` details are only available where the SDK exposes response headers — the
  JavaScript SDK's `error.headers`. Elsewhere, use a fixed backoff.
</Note>

***

## Support

Need something not covered here? Open a ticket with the operation you called, your
SDK version, and the request ID from the response headers.

<CardGroup cols={2}>
  <Card title="Dashboard" icon="layout-dashboard" href="https://dashboard.octaviatech.app" arrow="true">
    Manage API keys and inspect usage.
  </Card>

  <Card title="Support" icon="headset" href="https://support.octaviatech.app" arrow="true">
    Reach the engineering team.
  </Card>
</CardGroup>
