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

# Interface

> Branding, themes, colors, SEO and the empty-chat screen. Configure the chat product your agent ships with.

`cycls.Web` configures everything the user sees and the identity behind it. Pass
it to `@cycls.agent(web=...)`.

```python theme={null}
web = (
    cycls.Web()
    .auth(cycls.Clerk())
    .title("Atlas")
    .brand(name="Atlas", description="Your research assistant", logo="./logo.svg")
    .colors(primary="#FF6200", secondary="#1A1A1A")
    .suggestions(True)
    .analytics(True)
)
```

<Frame caption="The empty-chat screen uses the brand name, description and logo.">
  <img src="https://mintcdn.com/cycls/Vjgw_hSP7-Pr95Zb/img/agent-home.png?fit=max&auto=format&n=Vjgw_hSP7-Pr95Zb&q=85&s=41d51c128df30cb9fa730cccabecd02b" alt="A Cycls agent empty state with brand name, description and composer" width="1309" height="580" data-path="img/agent-home.png" />
</Frame>

## Branding

```python theme={null}
web = (
    cycls.Web()
    .brand(name="Atlas", description="Your research assistant",
           logo="./icon.svg", brand="./wordmark.svg",
           og="./og.png", favicon="./favicon.svg")
    .brand("ar", name="أطلس", description="مساعدك للبحث")
)
```

| Field         | Where it shows                                              |
| ------------- | ----------------------------------------------------------- |
| `name`        | the chat hero and the title bar                             |
| `description` | the line under the name on an empty chat                    |
| `logo`        | the agent icon in the hero                                  |
| `brand`       | the wordmark in the nav bar, falling back to the Cycls mark |
| `og`          | the social card image, global                               |
| `favicon`     | the tab icon, global                                        |

`name`, `description`, `logo` and `brand` are per locale, so call `.brand()`
again with `"ar"` for Arabic. Image paths are read at build time, SVGs inlined
and rasters embedded as data URIs, so assets do not need to ship with the
container.

## Pulling copy from a CMS

```python theme={null}
web = cycls.Web().cms(
    brand="https://cms.example.com/agents/atlas",
    explore="https://cms.example.com/agents",
    connectors="https://cms.example.com/connectors",
    token=os.environ["CMS_TOKEN"],
)
```

These are plain GET URLs returning the contract JSON. Anything declared in code
wins field by field, so a `.brand()` call overrides the CMS for exactly that
field and no other.

## Theme and colors

```python theme={null}
web = (
    cycls.Web()
    .theme("default")             # "default" or "dev"
    .colors(primary="#FF6200", secondary="#F5F5F5",
            primary_dark="#FF8A3D", secondary_dark="#1F1F1F")
)
```

`primary` drives highlights and active states, `secondary` drives chips and
bubbles. The `_dark` variants override dark mode and default to the light values.

## The empty-chat screen

<Tabs>
  <Tab title="Suggestions">
    ```python theme={null}
    web = cycls.Web().suggestions(True)
    ```

    Prompt starters, off by default.
  </Tab>

  <Tab title="Example gallery">
    ```python theme={null}
    web = cycls.Web().examples({
        ("Data analysis", "تحليل البيانات"): [
            "https://atlas.cycls.ai/shared/u_123/abc",
        ],
        "Landing pages": [
            "https://atlas.cycls.ai/shared/u_123/def",
            {"video": "/public/tour.mp4", "title": "Watch a two minute tour"},
        ],
    })
    ```

    Each entry is either a share URL of a real conversation this agent produced,
    which renders as an artifact card with Use prompt and View, or a video, which
    renders as a tutorial card. A tuple key gives the category pill both locales.
  </Tab>

  <Tab title="Explore menu">
    ```python theme={null}
    web = cycls.Web().explore(
        {"name": "Atlas", "url": "https://atlas.cycls.ai", "logo": "./atlas.svg"},
        {"name": "Scout", "url": "https://scout.cycls.ai", "name_ar": "كشاف"},
    )
    ```

    The agents dropdown in the header. A static list overrides the CMS list. With
    neither, the menu is hidden.
  </Tab>
</Tabs>

## SEO and social

```python theme={null}
web = (
    cycls.Web()
    .seo(title="Atlas, the research agent", description="Ask anything about your documents.")
    .head('<meta name="google-site-verification" content="..." />')
)
```

`.seo()` covers the page title and meta description when they should differ from
the brand copy. `.head()` appends raw HTML to `<head>` and is repeatable.

Every agent also serves `/og.png`, a generated social preview, plus
`/robots.txt`, `/sitemap.xml` and `/llms.txt`.

## Static files

```python theme={null}
web = cycls.Web().copy_public("./assets/logo.png", "./downloads/")
```

Served at `https://your-agent.cycls.ai/public/logo.png`.

## Uploads

```python theme={null}
web = cycls.Web().max_upload(256)   # MB per file, default 512
```

Enforced server-side and pre-checked in the client so oversized files fail fast.

## Analytics and push

```python theme={null}
web = (
    cycls.Web()
    .analytics(cycls.PostHog(), cycls.GTM("GTM-ABCD123"))
    .notifications(cycls.OneSignal("00000000-0000-0000-0000-000000000000"))
    .affiliate(os.environ["REWARDFUL_KEY"])
)
```

`.analytics(True)` is shorthand for the PostHog default. Providers are plugins
over one canonical event pipe, and each can be scoped with an `events` allowlist.
`.notifications()` adds web push, where the permission card is ours and the
trigger is a platform decision.

## Reacting to finished runs

```python theme={null}
async def on_run(record):
    if record["status"] == "failed":
        await page_oncall(record)


web = cycls.Web().on_run(on_run)
```

Called once when a run reaches any terminal status: done, stopped, interrupted or
failed. Use it for push, email, a webhook or a row in a table. Exceptions are
logged and never reach the run.

## Method reference

| Method                                        | Purpose                                                                       |
| --------------------------------------------- | ----------------------------------------------------------------------------- |
| `.auth(provider)`                             | [`cycls.Clerk()`](/web/auth) or `cycls.JWT(...)`                              |
| `.iap(config)`                                | [Apple in-app purchase](/web/monetization#apple-in-app-purchase) entitlements |
| `.title(str)`                                 | tab and app title                                                             |
| `.brand(locale, ...)`                         | static branding per locale                                                    |
| `.cms(brand=, explore=, connectors=, token=)` | pull copy from a CMS                                                          |
| `.theme(name)`                                | `"default"` or `"dev"`                                                        |
| `.colors(...)`                                | accent colors, light and dark                                                 |
| `.seo(title=, description=)`                  | page and social copy                                                          |
| `.head(html)`                                 | append to `<head>`, repeatable                                                |
| `.explore(*agents)`                           | the agents dropdown                                                           |
| `.examples(shares)`                           | the example gallery                                                           |
| `.suggestions(bool)`                          | prompt starters                                                               |
| `.analytics(*providers)`                      | PostHog, GTM, or `True` for the default                                       |
| `.notifications(*providers)`                  | web push plugins                                                              |
| `.affiliate(key)`                             | referral tracking                                                             |
| `.max_upload(mb)`                             | per-file upload cap                                                           |
| `.copy_public(*files)`                        | files served at `/public`                                                     |
| `.workspaces(create=)`                        | [multi-workspace mode](/web/workspaces)                                       |
| `.connectors(*objs)`                          | [connector directory](/agents/connectors)                                     |
| `.on_run(fn)`                                 | callback when a run ends                                                      |

## Next

<Card title="Authentication" icon="lock" href="/web/auth">
  Clerk, any OIDC provider, and what lands in `context.user`.
</Card>
