TypeScript SDK
@flung.dev/sdk is the typed client for the Flung API: every endpoint as a method, with retries, idempotency keys and typed errors. It runs anywhere with fetch and Web Crypto: Node 20 or newer, Cloudflare Workers, Deno and Bun.
TL;DR
| Question | Answer |
|---|---|
| How do I install it? | npm install @flung.dev/sdk |
| Which version is this page for? | 0.1.1 |
| Where does it run? | On a server. The API key it takes must never reach a browser; a page uses a connect token and the connect panel instead. |
| What does it depend on? | Nothing at run time. |
| How are methods named? | After the endpoint: POST /v1/posts is flung.posts.create(). Path, query and body fields go in one object. |
Install
npm install @flung.dev/sdkThe client
The Flung API client. Every resource of the API is a property: flung.posts.create({ content, accountIds }). Failed calls throw FlungError. Network failures, timeouts, 429 and 5xx answers are retried with backoff, honouring Retry-After. Every POST carries an Idempotency-Key (generated unless you pass one), so a retry never schedules a post twice.
Options
| Option | Type | Required | Description |
|---|---|---|---|
apiKey | string | yes | The credential: an API key (flung_sk_...), or an OAuth access token an app received through "Connect with Flung" (see Authentication in the docs). Keep either on a server; never ship it to a browser. |
baseUrl | string | no | Where the API lives. Defaults to production; pass https://api-preview.flung.dev to test against preview. |
maxRetries | number | no | Attempts after the first for network failures, timeouts, 429 and 5xx answers. Default 2. |
timeoutMs | number | no | How long one attempt may take before it is abandoned (and retried). Default 60 seconds. |
fetch | typeof fetch | no | A fetch to use instead of the global one, such as a test double. |
Helpers
Beyond one method per endpoint, the client has these.
uploadMedia(data: Blob \| Uint8Array \| ArrayBuffer, options: { contentType: string; filename?: string })
Uploads a file in one call and resolves with it ready, for mediaIds: reserves it, then sends the bytes, in parts when it is over 100 MB. Flung reads the real type from the file itself; contentType is what you expect.
const image = await flung.uploadMedia(await readFile('launch.png'), { contentType: 'image/png', filename: 'launch.png' })await flung.posts.create({ content: 'Launching today.', accountIds, mediaIds: [image.id] })waitForConnection(sessionId: string, options: { intervalMs?: number; timeoutMs?: number } = {})
Waits until the person finishes a connect or reconnect: resolves with the session once it is connected, failed or expired. Sessions last ten minutes, so this never waits longer than that unless timeoutMs says so.
const session = await flung.accounts.connect({ platform: 'linkedin' })console.warn(`Open ${session.authorizationUrl}`)const done = await flung.waitForConnection(session.id)paginate(list: (params: P & { startingAfter?: string }) => Promise<{ data: T[]; hasMore: boolean }>, params = {} as P)
Walks every page of a list: for await (const post of flung.paginate((page) => flung.posts.list(page))). Pass the first page's parameters (such as limit) as the second argument.
Errors
What every failed call throws. Branch on code, which never changes; message is for people and may.
try { await flung.posts.create({ content, accountIds }) }catch (error) { if (error instanceof FlungError && error.code === 'account_needs_reconnect') ... }Every code is listed in Errors.
Webhooks
Checks a webhook from Flung and returns the parsed event, or throws FlungWebhookError. Flung signs to the Standard Webhooks spec, so any Standard Webhooks library verifies its events too.
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 })}Input
| Field | Type | Required | Description |
|---|---|---|---|
body | string | yes | The raw request body, exactly as received. Parsing and re-serialising it changes the bytes and breaks the signature. |
headers | HeaderSource | yes | The request's headers, as a Fetch Headers or Node's req.headers; any casing works. |
secret | string | readonly string[] | yes | The endpoint's whsec_ secret. Pass two while you switch to a rotated secret; either one is accepted. |
nowSeconds | number | no | The current time in Unix seconds, for tests. |
Every method
flung.posts
| Method | Endpoint | What it does |
|---|---|---|
flung.posts.list() | GET /v1/posts | List posts |
flung.posts.create() | POST /v1/posts | Schedule a post |
flung.posts.get() | GET /v1/posts/{id} | Get a post |
flung.posts.update() | PATCH /v1/posts/{id} | Change a scheduled post |
flung.posts.delete() | DELETE /v1/posts/{id} | Delete a post |
flung.posts.cancel() | POST /v1/posts/{id}/cancel | Cancel a scheduled post |
flung.posts.retry() | POST /v1/posts/{id}/retry | Retry the failed accounts of a post |
flung.posts.metrics() | GET /v1/posts/{id}/metrics | Read a post's counts |
flung.accounts
| Method | Endpoint | What it does |
|---|---|---|
flung.accounts.list() | GET /v1/accounts | List connected accounts |
flung.accounts.get() | GET /v1/accounts/{id} | Get a connected account |
flung.accounts.update() | PATCH /v1/accounts/{id} | Change an account's settings |
flung.accounts.disconnect() | DELETE /v1/accounts/{id} | Disconnect an account |
flung.accounts.refresh() | POST /v1/accounts/{id}/refresh | Refresh an account now |
flung.accounts.boards() | GET /v1/accounts/{id}/boards | List Pinterest boards |
flung.accounts.postingOptions() | GET /v1/accounts/{id}/posting-options | Read what a post may choose |
flung.accounts.connect() | POST /v1/connect/{platform} | Start connecting an account |
flung.accounts.reconnect() | POST /v1/accounts/{id}/reconnect | Start reconnecting an account |
flung.accounts.syncInbox() | POST /v1/accounts/{id}/inbox/sync | Read an account's inbox now |
flung.webhookEndpoints
| Method | Endpoint | What it does |
|---|---|---|
flung.webhookEndpoints.list() | GET /v1/webhook-endpoints | List webhook endpoints |
flung.webhookEndpoints.create() | POST /v1/webhook-endpoints | Add a webhook endpoint |
flung.webhookEndpoints.rotateSecret() | POST /v1/webhook-endpoints/{id}/rotate-secret | Rotate a signing secret |
flung.webhookEndpoints.update() | PATCH /v1/webhook-endpoints/{id} | Change an endpoint |
flung.webhookEndpoints.delete() | DELETE /v1/webhook-endpoints/{id} | Delete a webhook endpoint |
flung.webhookEndpoints.ping() | POST /v1/webhook-endpoints/{id}/ping | Send a test event |
flung.webhookEndpoints.deliveries() | GET /v1/webhook-endpoints/{id}/deliveries | List deliveries |
flung.connectTokens
| Method | Endpoint | What it does |
|---|---|---|
flung.connectTokens.create() | POST /v1/connect-tokens | Make a connect token for a page |
flung.connectSessions
| Method | Endpoint | What it does |
|---|---|---|
flung.connectSessions.get() | GET /v1/connect-sessions/{id} | Get a connect session |
flung.apiKeys
| Method | Endpoint | What it does |
|---|---|---|
flung.apiKeys.list() | GET /v1/api-keys | List API keys |
flung.apiKeys.create() | POST /v1/api-keys | Create an API key |
flung.apiKeys.revoke() | DELETE /v1/api-keys/{id} | Revoke an API key |
flung.me
| Method | Endpoint | What it does |
|---|---|---|
flung.me.get() | GET /v1/me | Who is calling |
flung.members
| Method | Endpoint | What it does |
|---|---|---|
flung.members.list() | GET /v1/members | List members |
flung.members.add() | POST /v1/members | Add a member |
flung.members.remove() | DELETE /v1/members/{userId} | Remove a member |
flung.media
| Method | Endpoint | What it does |
|---|---|---|
flung.media.list() | GET /v1/media | List files |
flung.media.create() | POST /v1/media | Start an upload |
flung.media.uploadPart() | PUT /v1/media/{id}/parts/{partNumber} | Upload one part |
flung.media.complete() | POST /v1/media/{id}/complete | Finish a multipart upload |
flung.media.upload() | PUT /v1/media/{id}/content | Upload the bytes |
flung.media.import() | POST /v1/media/import | Import a file from a URL |
flung.media.get() | GET /v1/media/{id} | Get a file |
flung.media.delete() | DELETE /v1/media/{id} | Delete a file |
flung.templates
| Method | Endpoint | What it does |
|---|---|---|
flung.templates.list() | GET /v1/templates | List templates |
flung.templates.create() | POST /v1/templates | Create a template |
flung.templates.get() | GET /v1/templates/{id} | Get a template |
flung.templates.update() | PATCH /v1/templates/{id} | Change a template |
flung.templates.delete() | DELETE /v1/templates/{id} | Delete a template |
flung.templates.deploy() | POST /v1/templates/{id}/deploy | Schedule a post from a template |
flung.variables
| Method | Endpoint | What it does |
|---|---|---|
flung.variables.list() | GET /v1/variables | List variables |
flung.variables.set() | PUT /v1/variables/{name} | Set a variable |
flung.variables.delete() | DELETE /v1/variables/{name} | Delete a variable |
flung.feeds
| Method | Endpoint | What it does |
|---|---|---|
flung.feeds.list() | GET /v1/feeds | List feeds |
flung.feeds.create() | POST /v1/feeds | Follow an RSS or Atom feed |
flung.feeds.update() | PATCH /v1/feeds/{id} | Change a feed |
flung.feeds.delete() | DELETE /v1/feeds/{id} | Stop following a feed |
flung.platforms
| Method | Endpoint | What it does |
|---|---|---|
flung.platforms.list() | GET /v1/platforms | List platforms and their limits |
flung.platforms.postingTimes() | GET /v1/posting-times | Suggest when to post |
flung.analytics
| Method | Endpoint | What it does |
|---|---|---|
flung.analytics.usage() | GET /v1/usage | API requests per credential per day |
flung.analytics.stats() | GET /v1/stats | Workspace statistics |
flung.inbox
| Method | Endpoint | What it does |
|---|---|---|
flung.inbox.list() | GET /v1/inbox | List comments and mentions |
flung.inbox.get() | GET /v1/inbox/{id} | Get an inbox item |
flung.inbox.update() | PATCH /v1/inbox/{id} | Mark an item read, unread or archived |
flung.inbox.reply() | POST /v1/inbox/{id}/reply | Reply publicly |
flung.experience
| Method | Endpoint | What it does |
|---|---|---|
flung.experience.get() | GET /v1/experience | Read XP, level and achievements |
flung.experience.leaderboard() | GET /v1/leaderboard | Read the leaderboard |
flung.experience.joinLeaderboard() | PUT /v1/leaderboard/entry | Join the leaderboard |
flung.experience.leaveLeaderboard() | DELETE /v1/leaderboard/entry | Leave the leaderboard |
See also
| Doc | Why |
|---|---|
| Connect panel | Let people connect accounts from your page |
| API reference | Every endpoint, with samples |
| Webhooks | Receiving and verifying events |