# Add Flung to a website

> Page: https://docs.flung.dev/agents/build. Index of every page: https://docs.flung.dev/llms.txt

A recipe for a coding agent: add account connecting and posting to a web app with Flung. Follow the steps in order; each file below is from a working example app and can be copied as it is.

## TL;DR

| Question | Answer |
|---|---|
| What will the site do? | Show a panel where its users connect and disconnect social accounts, and post to those accounts from the server. |
| What must the person give me? | A Flung API key that starts `flung_sk_`. Keep it in a server environment variable, never in code a browser loads. |
| Which frameworks? | Next.js is shown. React with Vite and a plain page follow the same two parts: a server route that makes a connect token, and the panel that uses it. |
| How do I know it works? | The panel lists the platforms; connecting one opens the platform's page and the account appears; a post to it returns a post id. |

## 1. Install

```bash
npm install @flung.dev/sdk @flung.dev/connect
```

## 2. Keep the key on the server

Put the key in the server's environment. Never give it a public prefix such as `NEXT_PUBLIC_` or `VITE_`.

**.env.local**

```bash
# A full-access Flung API key (flung_sk_...). Server only: never prefix it with NEXT_PUBLIC_.
FLUNG_API_KEY=

# Optional. The API: https://api.flung.dev (the default) or https://api-preview.flung.dev.
FLUNG_BASE_URL=

# The origin this app is served from, which connect tokens are made for. http://localhost:3000 when left out.
APP_ORIGIN=
```

## 3. Make connect tokens on the server

Add a route the page calls. It checks who is signed in (add your app's own check), then asks Flung for a short-lived connect token for this site's origin.

**app/api/connect-token/route.ts**

```ts
import { Flung, FlungError } from '@flung.dev/sdk'

/**
 * `POST /api/connect-token`: a short-lived connect token for this app's own page. The API key stays here on the server;
 * the token it makes can only list, connect, reconnect and disconnect accounts, from `APP_ORIGIN`, for fifteen minutes.
 *
 * In a real app, check who is signed in first, and pass their profile as `profileId` so each person sees only theirs.
 */
export async function POST(): Promise<Response> {
  const apiKey = process.env.FLUNG_API_KEY
  if (apiKey === undefined || apiKey === '') return Response.json({ error: 'Set FLUNG_API_KEY in .env.local.' }, { status: 500 })
  const flung = new Flung({ apiKey, ...(process.env.FLUNG_BASE_URL ? { baseUrl: process.env.FLUNG_BASE_URL } : {}) })
  try {
    const { token, expiresAt } = await flung.connectTokens.create({ origins: [process.env.APP_ORIGIN || 'http://localhost:3000'], expiresIn: 900 })
    return Response.json({ token, expiresAt }, { headers: { 'cache-control': 'no-store' } })
  } catch (error) {
    const status = error instanceof FlungError ? (error.status ?? 502) : 502
    return Response.json({ error: error instanceof Error ? error.message : 'Could not make a connect token.' }, { status })
  }
}
```

## 4. Show the panel

Add the panel to a page. It asks the route for a token, and again when one expires.

**app/accounts.tsx**

```tsx
'use client'

import { useCallback, useState } from 'react'

import { FlungConnect } from '@flung.dev/connect/react'

/** Asks this app's server for a connect token; the panel calls it again when a token expires. */
async function connectToken(): Promise<string> {
  const response = await fetch('/api/connect-token', { method: 'POST' })
  const body = (await response.json()) as { token?: string; error?: string }
  if (!response.ok || body.token === undefined) throw new Error(body.error ?? 'Could not make a connect token.')
  return body.token
}

export function Accounts({ baseUrl }: { baseUrl: string | undefined }) {
  const [events, setEvents] = useState<string[]>([])
  const log = useCallback((line: string) => setEvents((previous) => [line, ...previous].slice(0, 5)), [])
  return (
    <>
      <FlungConnect
        token={connectToken}
        baseUrl={baseUrl}
        onConnect={({ accounts }) => log(`Connected ${accounts.map((account) => account.username ?? account.id).join(', ')}`)}
        onDisconnect={({ account }) => log(`Disconnected ${account.username ?? account.id}`)}
        onError={({ code, message }) => log(`${code}: ${message}`)}
      />
      {events.length > 0 && (
        <ul className="events" aria-label="What happened">
          {events.map((line, index) => (
            <li key={`${index}-${line}`}>{line}</li>
          ))}
        </ul>
      )}
    </>
  )
}
```

## 5. Post from the server

With the accounts connected, post to them with the API key, from the server.

**curl**

```bash
curl -X POST 'https://api.flung.dev/v1/posts' \
  --header "Authorization: Bearer $FLUNG_API_KEY" \
  --header 'Content-Type: application/json' \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{
  "content": "Launching today.",
  "accountIds": [
    "acct_aaaaaaaaaaaaaaaaaaaaaaaa"
  ],
  "platformOverrides": {
    "x": {
      "content": "Launching today. #launch"
    }
  },
  "variables": {
    "product": "Flung"
  }
}'
```

**TypeScript**

```ts
import { Flung } from '@flung.dev/sdk'

const flung = new Flung({ apiKey: process.env.FLUNG_API_KEY! })
const result = await flung.posts.create({
  content: 'Launching today.',
  accountIds: [
    'acct_aaaaaaaaaaaaaaaaaaaaaaaa',
  ],
  platformOverrides: {
    x: {
      content: 'Launching today. #launch',
    },
  },
  variables: {
    product: 'Flung',
  },
})
```

**fetch**

```js
const response = await fetch('https://api.flung.dev/v1/posts', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.FLUNG_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    content: 'Launching today.',
    accountIds: [
      'acct_aaaaaaaaaaaaaaaaaaaaaaaa',
    ],
    platformOverrides: {
      x: {
        content: 'Launching today. #launch',
      },
    },
    variables: {
      product: 'Flung',
    },
  }),
})
if (!response.ok) throw new Error((await response.json()).error.message)
const result = await response.json()
```

## 6. Hear what happened

Optionally, register a webhook endpoint and verify each event.

```ts
export async function POST(request: Request): Promise<Response> {
  const body = await request.text()
  const event = await verifyWebhook({ body, headers: request.headers, secret: process.env.FLUNG_WEBHOOK_SECRET! })
  if (event.type === 'post.failed') console.warn(`Post ${event.data.post.id} failed`)
  return new Response(null, { status: 204 })
}
```

## Checks before you finish

- `FLUNG_API_KEY` appears only in server code and the server's environment.
- The connect-token route is behind the app's own sign-in.
- `APP_ORIGIN` (or the origin you pass) is the address the page is served from, or the API refuses the panel's calls.
- Every POST that creates something sends an `Idempotency-Key`; the SDK does this for you.

## Other frameworks

| Framework | Read |
|---|---|
| React with Vite | [Add account connecting to your site](https://docs.flung.dev/connect-accounts.md#react-with-vite) |
| A plain page, no framework | [Add account connecting to your site](https://docs.flung.dev/connect-accounts.md#a-plain-page) |
| Anything else | Call the REST API: [API reference](https://docs.flung.dev/reference/api.md) |
