# Aether — Complete Documentation > Aether is a unified social media API platform. Developers integrate once > and get publish, schedule, inbox, automations, analytics, and webhooks > across social platforms via REST API and MCP server. API Base URL: https://api.aetherhq.dev/v1 Authentication: Authorization: Bearer aeth_live_ OpenAPI Spec: https://api.aetherhq.dev/v1/openapi.yaml --- ## Facebook URL: https://docs.aetherhq.dev/docs/platforms/facebook Facebook Page publishing, carousel posts, inbox, and comment-to-DM on Aether. Facebook Pages are fully supported in V1 — OAuth connect, publishing, inbox, and comment-to-DM via Meta's APIs. ## Connect a Page Facebook requires a **Page you manage** — personal profiles are not supported. 1. Create a Meta app with **Facebook Login** in the [Meta Developer Portal](https://developers.facebook.com/) 2. Set `FACEBOOK_APP_ID` and `FACEBOOK_APP_SECRET` on your Aether deployment 3. Generate a [Connect Link](/docs/guides/connect-links) with `"platform": "facebook"` If OAuth completes but you have no Pages, Aether creates a profile with `connectionStatus: "error"` and an `errorMessage` explaining the issue. Reconnect after creating a Page. If you manage **multiple Pages**, the dashboard shows a Page picker (`/profiles/connect/facebook`) so you can choose which Page to connect. Connect only works with Facebook Pages — not personal profiles. [Create a Page](https://www.facebook.com/pages/create) if you do not have one yet. Meta requires HTTPS redirect URIs. You need a public HTTPS URL — use any tunnel tool and register `https:///v1/oauth/callback/facebook`. ## Publishing Standard text and link posts: ```json { "text": "Check out our update", "profileIds": ["prof_fb456"], "overrides": { "facebook": { "mediaUrls": ["https://cdn.example.com/hero.jpg"] } } } ``` ## Carousel posts Use `carouselCards` (2–5 cards) or `carouselLink`: ```json { "overrides": { "facebook": { "carouselCards": [ { "link": "https://example.com/1", "name": "Product A" }, { "link": "https://example.com/2", "name": "Product B" } ] } } } ``` ## Page targeting Set `overrides.facebook.pageId` when a user manages multiple Pages and you need to target a specific one. ## Inbox and comment-to-DM Page messages and comments sync to the [unified inbox](/docs/guides/inbox). Use [automations](/docs/guides/automations) or `POST /inbox/comments/:platformPostId/:commentId/private-reply` for comment-to-DM flows. Private replies use the Messenger `/messages` API and require the `pages_messaging` OAuth scope. Reconnect existing Facebook profiles after upgrading to grant this permission. ## Development mode While your Meta app is in development mode, only accounts added as testers in the Meta portal can complete OAuth. --- ## Platforms URL: https://docs.aetherhq.dev/docs/platforms Supported social media platforms and availability in V1. Aether abstracts multiple social platforms behind one API. V1 supports OAuth connect and publishing for **Instagram**, **Facebook**, **Threads**, **TikTok**, and **LinkedIn**. Additional platforms are in development. | Platform | API value | Status | Guide | |----------|-----------|--------|-------| | Instagram | `instagram` | | [Instagram](/docs/platforms/instagram) | | Facebook | `facebook` | | [Facebook](/docs/platforms/facebook) | | Threads | `threads` | | [Threads](/docs/platforms/threads) | | TikTok | `tiktok` | | [TikTok](/docs/platforms/tiktok) | | LinkedIn | `linkedin` | | [LinkedIn](/docs/platforms/linkedin) | | YouTube | `youtube` | | — | | Reddit | `reddit` | | — | ## Connecting accounts Use [Connect Links](/docs/guides/connect-links) to connect Instagram, Facebook, Threads, TikTok, and LinkedIn accounts. Pass the platform value in `POST /v1/connect-links`. ## Per-platform overrides When publishing, use the `overrides` object keyed by platform to customize text, media, and platform-specific fields. See [Publishing](/docs/guides/publishing) and platform guides below. --- ## Instagram URL: https://docs.aetherhq.dev/docs/platforms/instagram Instagram publishing, stories, reels, inbox, and comment-to-DM on Aether. Instagram is fully supported in V1 — OAuth connect, publishing, inbox, analytics, and comment-to-DM automations. ## Connect an account 1. Create a Meta app with **Instagram API** product in the [Meta Developer Portal](https://developers.facebook.com/) 2. Set `INSTAGRAM_APP_ID` and `INSTAGRAM_APP_SECRET` on your Aether deployment 3. Generate a [Connect Link](/docs/guides/connect-links) with `"platform": "instagram"` Meta rejects `http://localhost` redirect URIs. You need a public HTTPS URL — use any tunnel tool (ngrok, frp, etc.) and register the HTTPS callback in the Meta portal. ## Content types Use `overrides.instagram.contentType`: | Value | Description | |-------|-------------| | `feed` | Standard feed post (default) | | `story` | Instagram Story | | `reel` | Instagram Reel | ```json { "text": "Behind the scenes", "profileIds": ["prof_ig123"], "overrides": { "instagram": { "contentType": "reel", "mediaUrls": ["https://cdn.example.com/reel.mp4"] } } } ``` ## Instagram-specific overrides | Field | Description | |-------|-------------| | `userTags` | Tag users with x/y coordinates on images | | `collaborators` | Up to 3 collaborator usernames | | `shareToFeed` | Share Reel to main feed | | `firstComment` | Auto-post first comment after publish | ## Media requirements - Images: JPEG, PNG — max dimensions per Meta guidelines - Video/Reels: MP4, recommended H.264 - Upload via [presigned URLs](/docs/guides/media) before attaching `mediaUrls` ## Inbox and DMs Instagram DMs and comments appear in the [unified inbox](/docs/guides/inbox). Reply via `POST /inbox/:id/reply` or send private replies with [automations](/docs/guides/automations). ## Webhooks (incoming) Live Meta webhook ingestion for comments is planned for Phase 3b. Until then, use automation test triggers and the private-reply API. --- ## LinkedIn URL: https://docs.aetherhq.dev/docs/platforms/linkedin LinkedIn OAuth connect, personal profile publishing, and scheduling on Aether. LinkedIn is supported in V1 for **OAuth connect, publishing, and scheduling** on **personal profiles** — text posts, images, and PDF documents. Company page posting, inbox, and analytics are not available yet. ## Connect an account 1. Generate a [Connect Link](/docs/guides/connect-links) with `"platform": "linkedin"` 2. Send the `url` to your user — they authorize via LinkedIn OAuth 3. After OAuth completes, the profile appears via `GET /v1/profiles` Set `LINKEDIN_CLIENT_ID` and `LINKEDIN_CLIENT_SECRET` on your API server. Enable **Sign In with LinkedIn using OpenID Connect** and **Share on LinkedIn** on your LinkedIn developer app. Register redirect URI `{NEXT_PUBLIC_API_URL}/v1/oauth/callback/linkedin` (e.g. `http://localhost:3001/v1/oauth/callback/linkedin` locally). ## Content types ### Text post ```json { "text": "We just shipped LinkedIn on Aether 🚀", "profileIds": ["prof_li123"] } ``` ### Image post ```json { "text": "New visual from the launch", "profileIds": ["prof_li123"], "mediaUrls": ["https://cdn.example.com/hero.jpg"] } ``` ### PDF document post ```json { "text": "Our Q1 report is live", "profileIds": ["prof_li123"], "mediaUrls": ["https://cdn.example.com/report.pdf"], "overrides": { "linkedin": { "documentTitle": "Q1 Report" } } } ``` ## Platform overrides | Field | Description | |-------|-------------| | `documentTitle` | Title shown on PDF document posts (max 255 chars) | | `disableLinkPreview` | Set `true` to disable link preview on text posts | | `organizationUrn` | Coming soon — company page posting requires LinkedIn Community Management API approval | ## First comment Pass `firstComment` at the post level or in `overrides.linkedin` to publish a comment immediately after the post goes live. ## Scheduling Pass `scheduledFor` with an optional `timezone` — same as other platforms. Posts are queued and published by the post worker. ## Rate limits LinkedIn enforces roughly **100 API calls per member per day** for posting-related endpoints. Exceeding the limit returns a platform error. ## Not available for LinkedIn yet | Feature | Status | |---------|--------| | Company page posting | Requires Community Management API (Phase 2) | | Articles and native video | Not implemented in connector | | Inbox / comment sync | Not supported | | Post and account analytics | Connector does not implement metrics fetch yet | ## Verification checklist After configuring your LinkedIn developer app and API env vars: 1. Dashboard → Profiles → Connect → LinkedIn (must be enabled) 2. Complete OAuth → profile appears with `platform: "linkedin"` 3. Publish a text post → visible on your LinkedIn feed 4. Publish an image post and a PDF document post 5. Optional: schedule a post ~10 minutes ahead and confirm it publishes on time | Symptom | Fix | |---------|-----| | LinkedIn grayed out in connect dialog | Rebuild `@aether/shared` after adding `linkedin` to `V1_PLATFORMS` | | `unauthorized_scope` on OAuth | Enable Share on LinkedIn + OIDC products in LinkedIn portal | | Redirect error | LinkedIn redirect URI must match `{NEXT_PUBLIC_API_URL}/v1/oauth/callback/linkedin` exactly | See [MCP Server](/docs/resources/mcp) and [Node.js SDK](/docs/resources/sdk) for connect and publish examples. --- ## Threads URL: https://docs.aetherhq.dev/docs/platforms/threads Threads OAuth connect, publishing, scheduling, and platform overrides on Aether. Threads is supported in V1 for **OAuth connect, publishing, and scheduling**. Inbox, analytics, and automations are available for Instagram and Facebook only today. ## Connect an account 1. Create a Meta app with the **Access the Threads API** use case in the [Meta Developer Portal](https://developers.facebook.com/) 2. Under **Use cases → Access the Threads API → Settings**, add your OAuth redirect URI (all three URL fields are required to save) 3. Add your Threads account as a **Threads Tester** and accept the invite under Threads → **Website permissions** → **Invites** 4. Set `THREADS_APP_ID` and `THREADS_APP_SECRET` on your Aether deployment (use the **Threads** app ID from the use case settings, not the generic app ID) 5. Generate a [Connect Link](/docs/guides/connect-links) with `"platform": "threads"` Meta rejects `http://localhost` redirect URIs. Use a public HTTPS tunnel URL and register `https:///v1/oauth/callback/threads` in the Threads API settings. ## Content types Threads supports text posts, single images, single videos, carousels (2–10 items), and multi-post threads via overrides. ```json { "text": "Launch day thread 🧵", "profileIds": ["prof_th123"], "mediaUrls": ["https://cdn.example.com/hero.jpg"] } ``` ## Threads-specific overrides Use `overrides.threads`: | Field | Description | |-------|-------------| | `topicTag` | Optional topic tag on the first post (max 50 characters) | | `threadItems` | Array of `{ text, mediaUrls? }` for a reply chain (max 10 posts). When set, each item is published in order. | | `firstComment` | Auto-post a first comment after publish (via platform `firstComment` on the post) | ```json { "text": "Thread intro (display only when threadItems is set)", "profileIds": ["prof_th123"], "overrides": { "threads": { "topicTag": "productlaunch", "threadItems": [ { "text": "First post in the thread" }, { "text": "Second post — published as a reply" } ] } } } ``` ## Cross-posting with Instagram Pass multiple `profileIds` in one `POST /posts` call to publish to Threads and Instagram simultaneously. Use per-platform `overrides` when formatting differs. ## Media requirements - Images: JPEG, PNG - Video: MP4 or MOV (up to 5 minutes) - Upload via [presigned URLs](/docs/guides/media) before attaching `mediaUrls` ## Rate limits Threads applies **250 API calls per 24 hours** per token. Aether queues scheduled posts and surfaces limit errors when a publish would exceed platform quotas. ## Not yet available for Threads | Feature | Status | |---------|--------| | Unified [Inbox](/docs/guides/inbox) | Instagram and Facebook only | | [Analytics](/docs/guides/analytics) | Instagram and Facebook only | | [Automations](/docs/guides/automations) | Instagram and Facebook only | --- ## TikTok URL: https://docs.aetherhq.dev/docs/platforms/tiktok TikTok OAuth connect, video and photo publishing, and scheduling on Aether. TikTok is supported in V1 for **OAuth connect, publishing, and scheduling** (video and Photo Mode). Inbox, analytics, and comment replies are available for Instagram and Facebook only today. ## Connect an account 1. Generate a [Connect Link](/docs/guides/connect-links) with `"platform": "tiktok"` 2. Send the `url` to your user — TikTok OAuth uses PKCE automatically 3. After OAuth completes, the profile appears via `GET /v1/profiles` Set `TIKTOK_CLIENT_KEY` and `TIKTOK_CLIENT_SECRET` on your API server. TikTok requires an approved developer app with `video.publish` and `video.upload` scopes. Aether's hosted API uses a pre-approved app — Connect Links work without your own TikTok developer credentials. ## Content types TikTok requires at least one media URL per post. Aether routes to video or photo publishing based on the file extension. ```json { "text": "POV: you shipped your TikTok integration in minutes 👀", "profileIds": ["prof_tt123"], "mediaUrls": ["https://cdn.example.com/clip.mp4"] } ``` Photo Mode (image carousel): ```json { "text": "Swipe through the launch shots", "profileIds": ["prof_tt123"], "mediaUrls": [ "https://cdn.example.com/slide-1.jpg", "https://cdn.example.com/slide-2.jpg" ] } ``` ## Scheduling Pass `scheduledFor` with an optional `timezone` — same as other platforms. Posts are queued and published by the post worker. ## Rate limits TikTok's Content Posting API enforces a **4 videos per 24 hours** quota per connected account. Exceeding the limit returns a platform error; schedule excess posts for the next window. ## Not available for TikTok yet | Feature | Status | |---------|--------| | Inbox / comment replies | Not supported — TikTok Content Posting API has no programmatic reply endpoint | | Post and account analytics | Connector does not implement metrics fetch yet | | First comment on publish | Not implemented for TikTok | See [MCP Server](/docs/resources/mcp) and [Node.js SDK](/docs/resources/sdk) for connect and publish examples. --- ## MCP Server URL: https://docs.aetherhq.dev/docs/resources/mcp Use Aether from Claude Desktop, Cursor, and any MCP-compatible AI client. Aether ships an MCP (Model Context Protocol) server that exposes API workflows as callable tools for AI clients. MCP covers profiles, posts, inbox, and analytics. **Queues** and **automations** are not yet exposed as MCP tools — use the REST API or dashboard. **V1 connect + publish:** Instagram, Facebook, Threads, TikTok, and LinkedIn. Inbox and analytics tools are useful for Instagram and Facebook today; other platforms' inbox and analytics are on the roadmap. ## Installation (stdio) ```json { "mcpServers": { "aether": { "command": "npx", "args": ["@aether/mcp"], "env": { "AETHER_API_KEY": "aeth_live_your_key_here", "AETHER_API_URL": "https://api.aetherhq.dev/v1" } } } } ``` ## Claude Desktop setup 1. Open Claude Desktop settings 2. Navigate to **Developer → MCP Servers** 3. Add the configuration above 4. Restart Claude Desktop ## Available tools (20) ### Profiles | Tool | Description | |------|-------------| | `list_profiles` | List connected social profiles | | `get_profile` | Get a profile by ID | | `disconnect_profile` | Disconnect a profile | | `create_connect_link` | Generate a Connect Link URL | ### Posts | Tool | Description | |------|-------------| | `create_post` | Create a draft, scheduled, or immediate post | | `list_posts` | List posts with filters | | `get_post` | Get post by ID | | `update_post` | Update a scheduled post | | `cancel_post` | Cancel a scheduled post | | `publish_post` | Create and publish immediately | | `schedule_post` | Create and schedule for a time | ### Inbox | Tool | Description | |------|-------------| | `list_messages` | List inbox messages | | `mark_message_read` | Mark a message read | | `get_inbox_summary` | Summarize unread messages | | `reply_to_message` | Reply to a DM or comment | ### Analytics | Tool | Description | |------|-------------| | `get_post_analytics` | Metrics for a post | | `get_account_analytics` | Metrics for a profile | | `get_best_posting_times` | Best times to post | | `get_performance_report` | 7 or 30-day performance report | ### Workflows | Tool | Description | |------|-------------| | `connect_account` | Generate Connect Link for a platform | ## Threads (V1) Threads is available for **OAuth connect, publishing, and scheduling** through MCP — the same tools you use for Instagram and Facebook. ### What works | Tool | Threads usage | |------|----------------| | `create_connect_link` / `connect_account` | `platform: "threads"` — returns a Connect Link URL | | `list_profiles` | Filter with `platform: "threads"` to get Threads `profileId`s | | `publish_post` | `platforms: ["threads"]` or pass a Threads `profileId` | | `schedule_post` | Same as `publish_post`, with `scheduledFor` | | `create_post` | Pass Threads `profileIds` and optional `overrides.threads` (`topicTag`, `threadItems`) | Example prompts for Claude or Cursor: ``` Connect a Threads account for my org ``` ``` Publish "Launch day 🚀" to my connected Threads profile now ``` ``` Schedule a post to Instagram and Threads for tomorrow at 9am UTC ``` ### What does not work yet | Tool | Threads status | |------|----------------| | `list_messages`, `reply_to_message`, `get_inbox_summary` | No Threads inbox ingestion — tools accept `platform: "threads"` but return no useful data | | `get_post_analytics`, `get_account_analytics`, `get_best_posting_times`, `get_performance_report` | Analytics pipeline is Instagram and Facebook only today | See the [Threads platform guide](/docs/platforms/threads) for OAuth setup, overrides, and rate limits. ## TikTok (V1) TikTok is available for **OAuth connect, publishing, and scheduling** through MCP — the same profile and post tools you use for other V1 platforms. ### What works | Tool | TikTok usage | |------|----------------| | `create_connect_link` / `connect_account` | `platform: "tiktok"` — returns a Connect Link URL (PKCE handled server-side) | | `list_profiles` | Filter with `platform: "tiktok"` to get TikTok `profileId`s | | `publish_post` | `platforms: ["tiktok"]` or pass a TikTok `profileId` | | `schedule_post` | Same as `publish_post`, with `scheduledFor` | | `create_post` | Pass TikTok `profileIds` and `mediaUrls` (video or images required) | Example prompts for Claude or Cursor: ``` Connect a TikTok account for my org ``` ``` Publish this video to TikTok now: https://cdn.example.com/clip.mp4 ``` ### What does not work yet | Tool | TikTok status | |------|----------------| | `list_messages`, `reply_to_message`, `get_inbox_summary` | No TikTok inbox ingestion | | `get_post_analytics`, `get_account_analytics`, `get_best_posting_times`, `get_performance_report` | Analytics pipeline is Instagram and Facebook only today | See the [TikTok platform guide](/docs/platforms/tiktok) for content types, rate limits, and connect setup. ## LinkedIn (V1) LinkedIn is available for **OAuth connect, publishing, and scheduling** on **personal profiles** through MCP — text, images, and PDF documents. ### What works | Tool | LinkedIn usage | |------|----------------| | `create_connect_link` / `connect_account` | `platform: "linkedin"` — returns a Connect Link URL | | `list_profiles` | Filter with `platform: "linkedin"` to get LinkedIn `profileId`s | | `publish_post` | `platforms: ["linkedin"]` or pass a LinkedIn `profileId` | | `schedule_post` | Same as `publish_post`, with `scheduledFor` | | `create_post` | Pass LinkedIn `profileIds`; optional `overrides.linkedin` (`documentTitle`, `disableLinkPreview`) | Example prompts for Claude or Cursor: ``` Connect a LinkedIn account for my org ``` ``` Publish "We shipped LinkedIn support today" to my connected LinkedIn profile ``` ``` Schedule a PDF report post to LinkedIn for tomorrow at 8am UTC ``` ### What does not work yet | Tool | LinkedIn status | |------|-----------------| | Company page posting | Requires Community Management API — use personal profile only in V1 | | `list_messages`, `reply_to_message`, `get_inbox_summary` | No LinkedIn inbox ingestion | | `get_post_analytics`, `get_account_analytics`, `get_best_posting_times`, `get_performance_report` | Analytics pipeline is Instagram and Facebook only today | See the [LinkedIn platform guide](/docs/platforms/linkedin) for content types, overrides, and verification steps. ## Error messages MCP tools return LLM-optimized errors with recovery hints — e.g. suggesting `list_profiles` when IDs are invalid. --- ## Node.js SDK URL: https://docs.aetherhq.dev/docs/resources/sdk Official Aether client library for Node.js and TypeScript. The official Node.js SDK (`aether` on npm) provides typed access to the Aether REST API. ## Installation ```bash npm install aether ``` ## Configuration ```typescript const client = new Aether({ apiKey: process.env.AETHER_API_KEY!, // baseUrl: "https://api.aetherhq.dev/v1", // optional override }); ``` Reads `AETHER_API_KEY` from the environment when passed to the constructor. ## Paginated lists `posts.list()`, `inbox.list()`, and `webhooks.listDeliveries()` return a `PaginatedList`: ```typescript const { items, total, hasMore } = await client.posts.list({ perPage: 20 }); ``` ## Resources | Resource | Methods | Guide | |----------|---------|-------| | `client.profiles` | `list`, `get`, `disconnect` | [Connect Links](/docs/guides/connect-links) | | `client.posts` | `list`, `create`, `get`, `update`, `cancel` | [Publishing](/docs/guides/publishing) | | `client.queues` | `list`, `create`, `get`, `update`, `delete`, `peekNextSlot` | [Queues](/docs/guides/queues) | | `client.automations` | `listCommentToDm`, `createCommentToDm`, `getCommentToDm`, `updateCommentToDm`, `deleteCommentToDm`, `testCommentToDm` | [Automations](/docs/guides/automations) | | `client.inbox` | `list`, `reply`, `markAsRead`, `sendPrivateReply` | [Inbox](/docs/guides/inbox) | | `client.analytics` | `getPostMetrics`, `getAccountMetrics`, `getBestPostingTimes` | [Analytics](/docs/guides/analytics) | | `client.webhooks` | `list`, `create`, `update`, `delete`, `deliveries` | [Webhooks](/docs/guides/webhooks) | | `client.connectLinks` | `list`, `create` | [Connect Links](/docs/guides/connect-links) | ## Error handling ```typescript try { await client.posts.create({ text: "Hi", profileIds: ["bad_id"] }); } catch (err) { if (err instanceof AetherError) { console.error(err.code, err.message, err.httpStatus); } } ``` ## Idempotency Pass `Idempotency-Key` on post creation via request options. The SDK automatically retries on `409 IDEMPOTENCY_IN_PROGRESS`. ## Not yet in SDK These API areas are available via REST but not yet wrapped in the Node SDK: - API keys (`/v1/api-keys`) - Organization and team (`/v1/organization`) - Media presign (`/v1/media/presign`) Use `fetch` or extend the client for these endpoints until SDK support ships. ## Threads (V1) Threads is supported for **connect and publish** via the Node SDK. Other language SDKs (Python, Go, Java, etc.) are not generated yet — use REST or the Node SDK until they ship. ### Connect ```typescript const link = await client.connectLinks.create({ platform: "threads", redirectUrl: "https://yourapp.com/settings/social", }); // Send link.url to your user; after OAuth, list profiles to get the profileId ``` ### Publish and schedule ```typescript await client.posts.create({ text: "Hello Threads", profileIds: [""], scheduledFor: "2026-06-15T12:00:00Z", // omit to publish immediately overrides: { threads: { topicTag: "productlaunch", threadItems: [ { text: "First post in the thread" }, { text: "Second post — published as a reply" }, ], }, }, }); ``` Cross-post in one call by including multiple `profileIds` (e.g. Instagram + Threads). The hand-maintained `PlatformOverride` type in the SDK currently exposes `text` and `mediaUrls`. Threads-specific fields (`topicTag`, `threadItems`) are fully supported by the REST API and work at runtime — use a type assertion or pass overrides through `fetch` if strict typing blocks you until the SDK is regenerated from OpenAPI. ### Not available for Threads yet - `client.inbox.*` — Instagram and Facebook only - `client.analytics.*` — Instagram and Facebook only - `client.automations.*` — Instagram and Facebook only See [Threads platform guide](/docs/platforms/threads) for Meta developer setup and content types. ## TikTok (V1) TikTok is supported for **connect and publish** via the Node SDK. Posts require at least one `mediaUrl` (video or images for Photo Mode). ### Connect ```typescript const link = await client.connectLinks.create({ platform: "tiktok", redirectUrl: "https://yourapp.com/settings/social", }); ``` ### Publish and schedule ```typescript await client.posts.create({ text: "POV: shipped in minutes 👀", profileIds: [""], mediaUrls: ["https://cdn.example.com/clip.mp4"], scheduledFor: "2026-06-15T18:00:00Z", // omit to publish immediately }); ``` ### Not available for TikTok yet - `client.inbox.*` — Instagram and Facebook only - `client.analytics.*` — Instagram and Facebook only - `client.automations.*` — Instagram and Facebook only See [TikTok platform guide](/docs/platforms/tiktok) for content types and rate limits. ## LinkedIn (V1) LinkedIn is supported for **connect and publish** on **personal profiles** via the Node SDK — text posts, images, and PDF documents. ### Connect ```typescript const link = await client.connectLinks.create({ platform: "linkedin", redirectUrl: "https://yourapp.com/settings/social", }); ``` ### Publish and schedule ```typescript // Text post await client.posts.create({ text: "Thoughts on shipping faster with a unified API", profileIds: [""], }); // PDF document post await client.posts.create({ text: "Our Q1 report is live", profileIds: [""], mediaUrls: ["https://cdn.example.com/report.pdf"], overrides: { linkedin: { documentTitle: "Q1 Report" }, }, scheduledFor: "2026-07-15T08:00:00Z", // omit to publish immediately }); ``` ### Not available for LinkedIn yet - Company page posting — Community Management API approval required (coming soon) - `client.inbox.*` — Instagram and Facebook only - `client.analytics.*` — Instagram and Facebook only - `client.automations.*` — Instagram and Facebook only See [LinkedIn platform guide](/docs/platforms/linkedin) for overrides, first comment, and rate limits. ## OpenAPI The SDK is maintained alongside [packages/openapi/openapi.yaml](https://api.aetherhq.dev/v1/openapi.yaml). See [API Reference](/docs/api-reference) for full request/response schemas. --- ## Quickstart URL: https://docs.aetherhq.dev/docs/overview/getting-started/quickstart Authenticate, connect accounts, and schedule your first post in minutes. Aether is a unified social media API — publish, schedule, manage inbox, and track analytics across platforms from a single REST API and MCP server. **Base URL:** `https://api.aetherhq.dev/v1` --- ## Install the SDK See the [Node.js SDK](/docs/resources/sdk) for full resource coverage. --- ## Authentication All API requests require an API key in the `Authorization` header: ```bash export AETHER_API_KEY=aeth_live_your_key_here ``` ### Getting your API key 1. Sign up at [app.aetherhq.dev](https://app.aetherhq.dev/signup) 2. Go to **API Keys** in the dashboard 3. Click **Create API Key** 4. Copy the key immediately — it is only shown once Keys use the `aeth_live_` prefix (production) or `aeth_test_` prefix (test). Keys are stored as hashes — the full value is shown only at creation. --- ## Key concepts | Concept | Description | |---------|-------------| | **Organization** | Top-level container for your team, profiles, posts, and API keys | | **SocialProfile** | A connected social media account (created via Connect Link OAuth) | | **Post** | Content published or scheduled to one or more SocialProfiles | | **ConnectLink** | Magic OAuth URL your end-user clicks to connect their account | | **PublishQueue** | Recurring time slots for automatic post scheduling | | **CommentAutomation** | Keyword rules that send private DMs when users comment | --- ## Step 1: List connected profiles After connecting an account (Step 2), list profiles to get `profileIds`: ```bash curl https://api.aetherhq.dev/v1/profiles \ -H "Authorization: Bearer $AETHER_API_KEY" ``` ```typescript const profiles = await client.profiles.list(); console.log(profiles.map((p) => `${p.platform}: ${p.id}`)); ``` --- ## Step 2: Connect a social account Generate a Connect Link and send the URL to your user: ```bash curl -X POST https://api.aetherhq.dev/v1/connect-links \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platform": "instagram", "label": "Main Instagram" }' ``` OAuth connect is available for **Instagram**, **Facebook**, **Threads**, **TikTok**, and **LinkedIn** in V1. Other platforms are coming soon — see [Platforms](/docs/platforms). Share the returned `url` with your user. After OAuth completes, the profile appears in your organization. --- ## Step 3: Schedule your first post ```bash curl -X POST https://api.aetherhq.dev/v1/posts \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Hello from Aether!", "profileIds": [""], "scheduledFor": "2026-06-15T12:00:00Z" }' ``` ```typescript const { post } = await client.posts.create({ text: "Hello from Aether!", profileIds: ["prof_abc123"], scheduledFor: "2026-06-15T12:00:00Z", }); ``` --- ## Publishing immediately Omit `scheduledFor` to publish right away (or use `POST /posts/:id/publish` on a draft): ```bash curl -X POST https://api.aetherhq.dev/v1/posts \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "This posts immediately!", "profileIds": [""] }' ``` --- ## Creating a draft Create a post without scheduling or publishing by saving it from the dashboard compose flow, or schedule it later with `PATCH /posts/:id`. --- ## What's next? - [Connect Links](/docs/guides/connect-links) — OAuth abstraction for end users - [Publishing](/docs/guides/publishing) — Cross-platform posts and overrides - [Queues](/docs/guides/queues) — Recurring posting schedules - [API Reference](/docs/api-reference) — Interactive endpoint docs - [MCP Server](/docs/resources/mcp) — Use Aether from Claude Desktop or Cursor --- ## Analytics URL: https://docs.aetherhq.dev/docs/overview/guides/analytics Post and account metrics powered by Tinybird. Aether serves analytics from Tinybird pipes — post-level metrics, account trends, and best posting times. Analytics requires Tinybird to be configured on your Aether deployment. Returns `503 ANALYTICS_UNAVAILABLE` when the service is not configured. ## Post metrics ```bash curl https://api.aetherhq.dev/v1/analytics/posts/{postId} \ -H "Authorization: Bearer $AETHER_API_KEY" ``` ```typescript const metrics = await client.analytics.getPostMetrics("post_abc123"); ``` ## Account metrics ```bash curl "https://api.aetherhq.dev/v1/analytics/accounts?profileId=prof_ig123&from=2026-06-01&to=2026-06-30&granularity=day" \ -H "Authorization: Bearer $AETHER_API_KEY" ``` | Parameter | Description | |-----------|-------------| | `profileId` | Filter to one SocialProfile | | `platform` | Filter by platform | | `from`, `to` | Date range (ISO dates) | | `granularity` | `day`, `week`, or `month` | ```typescript const data = await client.analytics.getAccountMetrics({ profileId: "prof_ig123", from: "2026-06-01", to: "2026-06-30", granularity: "day", }); ``` ## Best posting times ```bash curl "https://api.aetherhq.dev/v1/analytics/best-times?profileId=prof_ig123" \ -H "Authorization: Bearer $AETHER_API_KEY" ``` ```typescript const times = await client.analytics.getBestPostingTimes({ platform: "instagram" }); ``` ## Sync profile data Trigger a manual analytics sync for a profile: ```bash curl -X POST https://api.aetherhq.dev/v1/profiles/{profileId}/sync \ -H "Authorization: Bearer $AETHER_API_KEY" ``` See [Analytics API](/docs/api-reference/analytics). --- ## Authentication URL: https://docs.aetherhq.dev/docs/overview/guides/authentication API keys, scopes, and organization access for the Aether API. The Aether API uses API keys for authentication. Every request must include your key in the `Authorization` header as a Bearer token. ```bash Authorization: Bearer aeth_live_your_key_here ``` ## API key scopes | Scope | Description | |-------|-------------| | `full` | Read and write access to all resources | | `read_only` | Read access only — cannot create or modify resources | | `profiles_only` | Can only list and read connected social profiles | Keys can optionally be restricted to specific `allowedProfileIds` at creation time. ## Creating API keys From the dashboard: **API Keys → Create Key**. Each key has prefix `aeth_live_` (production) or `aeth_test_` (test) so you can identify the environment at a glance. The full key is shown **once** at creation. You can also manage keys via `POST /v1/api-keys` — see [API Reference](/docs/api-reference). ## Key rotation Revoke keys from the dashboard or `DELETE /v1/api-keys/:id`. Revoked keys return `401 Unauthorized` immediately. ## Rate limits Rate limits apply per API key. Response headers on every request: | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Requests allowed per minute | | `X-RateLimit-Remaining` | Requests remaining in the current window | | `X-RateLimit-Reset` | Unix timestamp when the window resets | On `429 Too Many Requests`, respect the `Retry-After` header before retrying. ## Dashboard sessions The developer dashboard uses JWT session auth (`POST /v1/auth/signin`) — separate from API keys. API documentation covers API key access only. --- ## Automations URL: https://docs.aetherhq.dev/docs/overview/guides/automations Comment-to-DM and story-reply automation rules with keyword matching. Keyword automations on Instagram and Facebook. Two triggers are supported: | Trigger | When it runs | Response | |---------|--------------|----------| | `comment` | A comment matches your keywords | Optional public reply + private DM to the commenter | | `story_reply` | Someone replies to your story with matching text | Direct message via the Messaging Send API | Comment and story-reply automations run automatically from Meta webhooks. Use `POST /automations/comment-to-dm/:id/test` to validate without waiting for a live event. ## Create a comment automation ```bash curl -X POST https://api.aetherhq.dev/v1/automations/comment-to-dm \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileId": "prof_ig123", "name": "Guide keyword DM", "trigger": "comment", "keywords": ["guide", "ebook"], "matchMode": "contains", "dmMessage": "Thanks for commenting! Here is your guide: https://example.com/guide", "commentReply": "Check your DMs!" }' ``` ## DM buttons in private replies Attach 1–3 inline buttons to the private DM (`url` opens a link; `postback` sends a payload back to your webhook). Button titles are limited to 20 characters. When `buttons` are set, `dmMessage` must be **640 characters or fewer** (Meta button template limit). ```bash curl -X POST https://api.aetherhq.dev/v1/automations/comment-to-dm \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileId": "prof_ig123", "name": "Guide with CTA", "keywords": ["guide"], "matchMode": "contains", "dmMessage": "Thanks! Tap below for your guide:", "buttons": [ { "type": "url", "title": "Get guide", "url": "https://example.com/guide" } ] }' ``` Private replies use the Messenger `/messages` API. Facebook profiles connected before this feature need OAuth reconnect to grant `pages_messaging`. See [Facebook platform guide](/docs/platforms/facebook). ## Create a story-reply automation Set `"trigger": "story_reply"`. Do not set `commentReply` — story replies are answered with a DM only. ```bash curl -X POST https://api.aetherhq.dev/v1/automations/comment-to-dm \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileId": "prof_ig123", "name": "Story link DM", "trigger": "story_reply", "platformPostId": "1791234567890", "keywords": ["link"], "matchMode": "contains", "dmMessage": "Here is the link: https://example.com" }' ``` Omit `platformPostId` to match story replies on any active story for the profile. Set it to scope the rule to one story media ID. Instagram webhooks reliably include `reply_to.story.id`. Facebook Page messaging may omit story metadata, so auto-triggering `story_reply` automations on Facebook is best-effort. Manual tests with `recipientId` always work. ```typescript const automation = await client.automations.createCommentToDm({ profileId: "prof_ig123", name: "Story link DM", trigger: "story_reply", keywords: ["link"], matchMode: "contains", dmMessage: "Here is your link: https://example.com", }); ``` ## Match modes | Mode | Behavior | |------|----------| | `contains` | Text contains any keyword (case-insensitive) | | `exact` | Text exactly matches a keyword | ## Scope to a post or story Set `platformPostId` to run the automation only for a specific post (comments) or story media ID (story replies). Omit to match account-wide. ## Test manually **Comment trigger** — requires `commentId`: ```bash curl -X POST https://api.aetherhq.dev/v1/automations/comment-to-dm/{id}/test \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platformPostId": "1791234567890", "commentId": "1789876543210", "commentText": "I want the guide" }' ``` **Story-reply trigger** — requires `recipientId` (IGSID or Facebook PSID), not `commentId`: ```bash curl -X POST https://api.aetherhq.dev/v1/automations/comment-to-dm/{id}/test \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platformPostId": "1791234567890", "commentText": "send me the link", "recipientId": "1234567890" }' ``` ## Private reply API For one-off private replies without an automation rule (comment trigger only): ```bash curl -X POST https://api.aetherhq.dev/v1/inbox/comments/{platformPostId}/{commentId}/private-reply \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileId": "prof_ig123", "message": "Thanks for reaching out!" }' ``` ## Webhook events | Event | When | |-------|------| | `inbox.comment_received` | A comment is ingested | | `inbox.story_reply_received` | A story reply is ingested | | `automation.triggered` | An automation runs successfully (includes `trigger` field) | See [Automations API](/docs/api-reference/automations). --- ## Connect Links URL: https://docs.aetherhq.dev/docs/overview/guides/connect-links Magic OAuth URLs that let users connect social accounts without implementing OAuth. Connect Links are Aether's account-connection flow. Generate a URL, send it to your user, and when they complete OAuth the SocialProfile appears in your organization. ## How it works 1. Call `POST /v1/connect-links` to generate a link 2. Send the `url` to your user (email, in-app button, etc.) 3. The user completes OAuth with the platform 4. Aether redirects to your `redirectUrl` (optional) or the dashboard 5. The new SocialProfile is available via `GET /v1/profiles` ## Creating a Connect Link ```bash curl -X POST https://api.aetherhq.dev/v1/connect-links \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platform": "instagram", "label": "Main Instagram", "redirectUrl": "https://yourapp.com/accounts/connected" }' ``` ```typescript const link = await client.connectLinks.create({ platform: "instagram", label: "Main Instagram", redirectUrl: "https://yourapp.com/accounts/connected", }); console.log(link.url); ``` ## Link expiry Connect Links expire after **24 hours** and can only be used **once**. Generate a new link if the user needs to reconnect. ## Supported platforms (V1) | Platform | API value | Status | |----------|-----------|--------| | Instagram | `instagram` | Available | | Facebook | `facebook` | Available | | Threads | `threads` | Available | | TikTok | `tiktok` | Available | | LinkedIn | `linkedin` | Available | YouTube and Reddit connectors exist but OAuth connect is not yet enabled. See [Platforms](/docs/platforms). ## Webhook events - `connect_link.used` — fired when a user completes OAuth via the link - `connect_link.expired` — fired when a link expires unused See [Webhooks](/docs/guides/webhooks) for signature verification. --- ## Errors URL: https://docs.aetherhq.dev/docs/overview/guides/errors API error format, common codes, and how to handle failures. All API errors use a consistent envelope: ```json { "success": false, "error": { "code": "PROFILE_NOT_FOUND", "message": "Social profile not found." } } ``` Some errors include a `details` object with additional context. ## HTTP status codes | Status | Meaning | |--------|---------| | `400` | Invalid request body or query parameters | | `401` | Missing or invalid API key | | `403` | Insufficient scope or permission | | `404` | Resource not found | | `409` | Conflict (e.g. idempotency in progress) | | `422` | Validation failed | | `429` | Rate limit exceeded | | `500` | Internal server error | | `503` | Dependency unavailable (e.g. analytics) | ## Common error codes | Code | Description | |------|-------------| | `VALIDATION_ERROR` | Request body failed schema validation | | `INVALID_QUERY_PARAMS` | Query string validation failed | | `PROFILE_NOT_FOUND` | SocialProfile ID does not exist | | `POST_NOT_FOUND` | Post ID does not exist | | `QUEUE_NOT_FOUND` | Publish queue ID does not exist | | `AUTOMATION_NOT_FOUND` | Automation ID does not exist | | `MESSAGE_NOT_FOUND` | Inbox message ID does not exist | | `IDEMPOTENCY_IN_PROGRESS` | Duplicate request still processing — retry with backoff | | `ANALYTICS_UNAVAILABLE` | Tinybird analytics not configured or down | | `STORAGE_UNAVAILABLE` | Media storage not configured | | `INTERNAL_ERROR` | Unexpected server error | ## Idempotency `POST /v1/posts` supports idempotency via `Idempotency-Key` header. On `409 IDEMPOTENCY_IN_PROGRESS`, wait and retry — the Node SDK retries automatically. ## SDK errors The Node SDK throws `AetherError` with `code`, `message`, and optional `httpStatus`: ```typescript try { await client.posts.get("invalid_id"); } catch (err) { if (err instanceof AetherError) { console.log(err.code, err.httpStatus); } } ``` ## MCP errors MCP tools return human-readable error text optimized for LLM recovery — e.g. suggesting `list_profiles` when profile IDs are invalid. --- ## Inbox URL: https://docs.aetherhq.dev/docs/overview/guides/inbox Unified inbox for DMs, comments, mentions, and replies across platforms. The unified inbox aggregates DMs, comments, mentions, and reviews from connected profiles into a single API. ## List messages ```bash curl "https://api.aetherhq.dev/v1/inbox?platform=instagram&type=dm&isRead=false" \ -H "Authorization: Bearer $AETHER_API_KEY" ``` Query parameters: | Parameter | Description | |-----------|-------------| | `platform` | Filter by platform (`instagram`, `facebook`, etc.) | | `type` | `dm`, `comment`, `mention`, or `review` | | `isRead` | `true` or `false` | | `profileId` | Filter by SocialProfile ID | | `page`, `perPage` | Pagination | ```typescript const messages = await client.inbox.list({ platform: "instagram", type: "dm", isRead: false, }); ``` ## Reply to a message `POST /v1/inbox/{messageId}/reply` routes by message type: | Type | Behavior | |------|----------| | `comment` | Public comment reply on the post | | `dm`, `story_reply` | Direct message via Meta Messaging API (Instagram/Facebook only) | | `mention`, `review` | Not supported (`422 REPLY_UNSUPPORTED`) | For DMs and story replies, Aether uses the sender's platform user ID captured during webhook ingestion. Meta allows replies within **24 hours** of the user's last message; older threads return `422 DM_OUTSIDE_24H_WINDOW`. ```bash curl -X POST https://api.aetherhq.dev/v1/inbox/{messageId}/reply \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "Thanks for your message!" }' ``` ## Mark as read ```bash curl -X PATCH https://api.aetherhq.dev/v1/inbox/{messageId}/read \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "isRead": true }' ``` ## Private reply to a comment Send a DM in response to a comment (Instagram/Facebook): ```bash curl -X POST https://api.aetherhq.dev/v1/inbox/comments/{platformPostId}/{commentId}/private-reply \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileId": "prof_ig123", "message": "Sent you the details in DM!" }' ``` With inline buttons (recommended for Instagram Message Requests — cold reach): ```bash curl -X POST https://api.aetherhq.dev/v1/inbox/comments/{platformPostId}/{commentId}/private-reply \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileId": "prof_ig123", "message": "Thanks for commenting! Tap below:", "buttons": [ { "type": "url", "title": "Get link", "url": "https://example.com" } ] }' ``` When `buttons` are present, `message` must be 640 characters or fewer. See [Automations](/docs/guides/automations) for keyword-triggered private replies. ## Webhook events - `inbox.message_received` — new DM or inbox item - `inbox.comment_received` — new comment See [Inbox API](/docs/api-reference/inbox). --- ## Media URL: https://docs.aetherhq.dev/docs/overview/guides/media Upload images and videos via presigned URLs for use in posts. Posts reference media by public URL. Use presigned uploads to put files on Aether's storage (Cloudflare R2 or any S3-compatible provider) before publishing. ## Presigned upload flow 1. Request a presigned PUT URL 2. Upload the file directly to storage with `PUT` 3. Use the returned public URL in `mediaUrls` when creating a post ## Get a presigned URL ```bash curl -X POST https://api.aetherhq.dev/v1/media/presign \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "filename": "photo.jpg", "contentType": "image/jpeg" }' ``` Response includes `uploadUrl`, `publicUrl`, and `key` (canonical storage key stored on posts as `mediaKeys`). ```typescript // Presign is not yet in the Node SDK — use fetch or add via raw request const res = await fetch("https://api.aetherhq.dev/v1/media/presign", { method: "POST", headers: { Authorization: `Bearer ${process.env.AETHER_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ filename: "photo.jpg", contentType: "image/jpeg" }), }); const { data } = await res.json(); ``` ## Upload the file ```bash curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: image/jpeg" \ --data-binary @photo.jpg ``` ## Attach to a post ```json { "text": "New visual", "profileIds": ["prof_ig123"], "mediaUrls": ["https://cdn.aetherhq.dev/org-id/uuid.jpg"] } ``` At publish time, Aether can re-resolve URLs from `mediaKeys` if your storage domain changes. ## Dashboard upload The dashboard uses `POST /v1/media/upload` (multipart proxy) for convenience. API integrations should prefer presigned uploads. See [Media API](/docs/api-reference/media). --- ## Publishing URL: https://docs.aetherhq.dev/docs/overview/guides/publishing Cross-platform posting, scheduling, queues, and per-platform content overrides. Publish to multiple SocialProfiles with a single API call. Use per-platform `overrides` to customize content per platform. ## Creating a post ```bash curl -X POST https://api.aetherhq.dev/v1/posts \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Big announcement coming soon!", "profileIds": ["prof_ig123", "prof_fb456"] }' ``` ```typescript const { post } = await client.posts.create({ text: "Big announcement coming soon!", profileIds: ["prof_ig123", "prof_fb456"], }); ``` ## Scheduling Add `scheduledFor` as ISO 8601 datetime. Optionally set `timezone` for display: ```json { "text": "Good morning!", "profileIds": ["prof_ig123"], "scheduledFor": "2026-06-20T09:00:00-05:00", "timezone": "America/New_York" } ``` ## Queue scheduling Assign the next available slot from a [Publish Queue](/docs/guides/queues): ```json { "text": "Queued post", "profileIds": ["prof_ig123"], "queueId": "queue_abc123" } ``` Or use `useDefaultQueue: true` to reserve the next slot on your org's default queue. ## Per-platform overrides ```json { "text": "Check out our latest update", "profileIds": ["prof_ig123", "prof_fb456"], "overrides": { "instagram": { "text": "New drop just landed — link in bio", "mediaUrls": ["https://cdn.example.com/product.jpg"], "contentType": "feed" }, "facebook": { "text": "We're excited to announce our latest product update." } } } ``` ## Bulk create Create multiple posts in one request: ```bash curl -X POST https://api.aetherhq.dev/v1/posts/bulk \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "posts": [ { "text": "Post 1", "profileIds": ["prof_ig123"] } ] }' ``` ## Publish and retry (REST) These endpoints are available via REST but not yet in the Node SDK: - `POST /v1/posts/:id/publish` — publish a draft or re-publish to specific profiles - `POST /v1/posts/:id/retry` — retry failed platform results on a `failed` post - `POST /v1/posts/bulk` — create multiple posts in one request ## Post statuses | Status | Description | |--------|-------------| | `draft` | Not yet published or scheduled | | `scheduled` | Queued for future publishing | | `publishing` | Currently being published | | `published` | Successfully published | | `failed` | Publishing failed — check `publishResults` | | `cancelled` | Cancelled before publishing | ## Media Upload media via [presigned URLs](/docs/guides/media), then attach `mediaUrls` to your post. ## API reference See [Posts](/docs/api-reference/posts) in the interactive API reference. --- ## Queues URL: https://docs.aetherhq.dev/docs/overview/guides/queues Org-level recurring publish slot schedules for automatic post timing. Publish Queues define recurring time slots (e.g. Mon/Wed/Fri at 09:00) so posts can be scheduled into the next available slot automatically. ## Create a queue ```bash curl -X POST https://api.aetherhq.dev/v1/queues \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Weekday mornings", "timezone": "America/New_York", "slots": [ { "dayOfWeek": 1, "time": "09:00" }, { "dayOfWeek": 3, "time": "09:00" }, { "dayOfWeek": 5, "time": "09:00" } ], "isDefault": true }' ``` ```typescript const queue = await client.queues.create({ name: "Weekday mornings", timezone: "America/New_York", slots: [ { dayOfWeek: 1, time: "09:00" }, { dayOfWeek: 3, time: "09:00" }, { dayOfWeek: 5, time: "09:00" }, ], isDefault: true, }); ``` `dayOfWeek` is `0` (Sunday) through `6` (Saturday). `time` is 24-hour `HH:MM` in the queue's timezone. ## Schedule a post into a queue When creating a post, pass `queueId` or `useDefaultQueue`: ```typescript await client.posts.create({ text: "Next slot post", profileIds: ["prof_ig123"], queueId: queue.id, }); ``` Aether reserves the next available slot and sets `scheduledFor` on the post. A `post.scheduled` webhook fires. ## Preview next slots ```bash curl "https://api.aetherhq.dev/v1/queues/next-slot?queueId=queue_abc123&count=5" \ -H "Authorization: Bearer $AETHER_API_KEY" ``` ## Profile filtering Optionally restrict a queue to specific profiles with `profileIds`. Posts targeting profiles outside the queue's filter cannot use that queue. ## Manage queues | Endpoint | Action | |----------|--------| | `GET /v1/queues` | List queues | | `GET /v1/queues/:id` | Get queue | | `PATCH /v1/queues/:id` | Update slots, timezone, active state | | `DELETE /v1/queues/:id` | Delete queue | See [Queues API](/docs/api-reference/queues). --- ## Webhooks URL: https://docs.aetherhq.dev/docs/overview/guides/webhooks Receive real-time event notifications from Aether via HTTP callbacks. Webhooks deliver signed JSON payloads to your server when events occur — posts publish, profiles connect, inbox messages arrive, and more. ## Creating a webhook ```bash curl -X POST https://api.aetherhq.dev/v1/webhooks \ -H "Authorization: Bearer $AETHER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/webhooks/aether", "events": ["post.published", "post.failed"], "description": "Production webhook" }' ``` The response includes `secret` — **shown exactly once**. Store it securely. ## Verifying signatures Every request includes `X-Aether-Signature: sha256=`. **Algorithm:** `HMAC-SHA256(key=plainSecret, message=rawRequestBody)` ```typescript function verifyAetherWebhook( rawBody: string, signature: string, secret: string, ): boolean { const expected = `sha256=${crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex")}`; return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected), ); } ``` Verify the **raw** body before JSON parsing. ## Request headers | Header | Value | |--------|-------| | `Content-Type` | `application/json` | | `X-Aether-Signature` | `sha256=` | | `X-Aether-Event` | Event type | | `X-Aether-Delivery` | Unique delivery ID | | `User-Agent` | `Aether-Webhooks/1.0` | ## Event reference ### Post lifecycle | Event | Description | |-------|-------------| | `post.published` | Post published to all target profiles | | `post.failed` | All target profiles failed | | `post.scheduled` | Post scheduled for future publish | | `post.cancelled` | Scheduled post cancelled | | `post.platform.publishing` | Single platform publish started | | `post.platform.published` | Single platform publish succeeded | | `post.platform.failed` | Single platform publish failed | ### Inbox | Event | Description | |-------|-------------| | `inbox.message_received` | New DM or inbox item | | `inbox.comment_received` | New comment | ### Automations | Event | Description | |-------|-------------| | `automation.triggered` | Comment-to-DM automation ran | ### Profiles | Event | Description | |-------|-------------| | `profile.connected` | Account connected via OAuth | | `profile.disconnected` | Account disconnected | | `profile.token_expired` | Token refresh failed | | `profile.error` | Profile entered error state | ### Connect links | Event | Description | |-------|-------------| | `connect_link.used` | Connect link consumed | | `connect_link.expired` | Connect link expired unused | ## Retries Non-2xx responses trigger up to **5 retries** with exponential backoff (~1s, 2s, 4s, 8s). Return `200` quickly and process asynchronously. ## Delivery logs ```bash curl https://api.aetherhq.dev/v1/webhooks/{webhookId}/deliveries \ -H "Authorization: Bearer $AETHER_API_KEY" ``` See [Webhooks API](/docs/api-reference/webhooks). --- ## Health URL: https://docs.aetherhq.dev/docs/api-reference/health Check Aether API service health and availability. Returns database and Redis connection status in real-time. (Content generated from OpenAPI specification) --- ## Profiles URL: https://docs.aetherhq.dev/docs/api-reference/profiles Manage connected social media accounts across Instagram, Facebook, TikTok, LinkedIn, YouTube, Threads, and Reddit via OAuth abstraction. (Content generated from OpenAPI specification) --- ## Posts URL: https://docs.aetherhq.dev/docs/api-reference/posts Publish and schedule social media posts across 7 platforms. Supports per-platform overrides, media attachments, and timezone-aware scheduling. (Content generated from OpenAPI specification) --- ## Inbox URL: https://docs.aetherhq.dev/docs/api-reference/inbox Unified inbox for DMs, comments, and mentions across all connected social platforms. Read and reply to messages from a single endpoint. (Content generated from OpenAPI specification) --- ## Analytics URL: https://docs.aetherhq.dev/docs/api-reference/analytics Query post and account metrics across connected social platforms. Powered by Tinybird for sub-second analytics and normalized schema. (Content generated from OpenAPI specification) --- ## Webhooks URL: https://docs.aetherhq.dev/docs/api-reference/webhooks Manage webhook endpoints and delivery logs. HMAC-signed events for post.published, post.failed, comment.created, and message.created. (Content generated from OpenAPI specification) --- ## Connect Links URL: https://docs.aetherhq.dev/docs/api-reference/connect-links Generate magic OAuth connect links for end-user social account connection without exposing your credentials or Aether branding. (Content generated from OpenAPI specification) --- ## O Auth URL: https://docs.aetherhq.dev/docs/api-reference/oauth OAuth connect flows (Facebook Page selection after multi-Page authorization) (Content generated from OpenAPI specification) --- ## A P I Keys URL: https://docs.aetherhq.dev/docs/api-reference/api-keys Create, list, and revoke API keys for authenticating with the Aether REST API. Supports scoped keys and per-profile access restrictions. (Content generated from OpenAPI specification) --- ## Organizations URL: https://docs.aetherhq.dev/docs/api-reference/organizations Manage organizations, team members, roles, and invitations. Organizations are the top-level billing and access control unit in Aether. (Content generated from OpenAPI specification) --- ## Media URL: https://docs.aetherhq.dev/docs/api-reference/media Upload media files to cloud storage via presigned URLs. Returns a media key for use in post creation across all connected platforms. (Content generated from OpenAPI specification) --- ## Queues URL: https://docs.aetherhq.dev/docs/api-reference/queues Configure recurring publish slot schedules for an organization. Posts added to a queue are automatically published at the scheduled times. (Content generated from OpenAPI specification) --- ## Automations URL: https://docs.aetherhq.dev/docs/api-reference/automations Configure comment-to-DM automation rules that trigger private replies when incoming comments match defined keyword patterns. (Content generated from OpenAPI specification) ---