AI agents
Add Flung to a website
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/connect2. 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.
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" }}'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', },})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_KEYappears 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 |
| A plain page, no framework | Add account connecting to your site |
| Anything else | Call the REST API: API reference |