# NOWScale API > Schedule and publish posts, upload media, answer comments and read analytics from your own code or AI tools. The NOWScale API lets your code do what your team does in the app: create and schedule posts for every connected network, upload videos and images, follow each post until it is live, read analytics, and reply to comments. Webhooks tell your server the moment something changes. Everything happens inside a Workspace, with the same Projects, approvals and plan limits as the app. A key can never do more than the person or Workspace it belongs to. ## Base URL ```text https://api.nowscale.com/v1 ``` Every request needs an API key in the `Authorization` header. Responses are JSON; errors use [problem details](/errors). ```bash curl https://api.nowscale.com/v1/me \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ## Where to start - [Quickstart](/quickstart): create a key and make your first calls in five minutes. - [Authentication](/authentication): Workspace keys, personal keys, permissions and IP rules. - [Publish posts](/posts): drafts, schedules, approvals and per-network settings. - [Upload media](/media): single uploads for images, parts for large videos. - [Webhooks](/webhooks): signed events for posts and more. - [API reference](/reference/posts): every endpoint with its parameters and responses. ## Who can use it The API is included in every paid plan (Creator, Pro, Business, Agency and Enterprise). Requests per minute, keys, webhook endpoints and sandboxes grow with the plan; see [Rate limits](/rate-limits). :::note The API is rolling out in stages. If Settings does not show **API keys** yet, your Workspace is not in the current stage. ::: ## Using these docs with AI Every page is also plain Markdown: add `.md` to its address, or send `Accept: text/markdown`. [llms.txt](/llms.txt) lists every page, and [llms-full.txt](/llms-full.txt) is the whole documentation in one file. See [Use with AI tools](/ai). Source: https://docs.nowscale.dev/ --- # Quickstart > Create an API key, check it, and schedule your first post. ## 1. Create a key In NOWScale, open **Settings**, choose your Workspace and open **API keys**. Select **Create key**, give it a name, pick what it can do (the **Publishing** preset covers this guide) and copy the key. It is shown once. Keep the key on your server, in an environment variable: ```bash export NOWSCALE_API_KEY="ns_live_..." ``` :::warning Never put a key in a browser, a mobile app or a public repository. Anyone with the key can act as it. ::: ## 2. Check the key `GET /v1/me` returns the key, who it acts as and your plan's limits. ```bash curl https://api.nowscale.com/v1/me \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ```json { "object": "api_key", "kind": "workspace", "scopes": ["accounts:read", "media:write", "posts:write", "posts:publish"], "acting_as": { "type": "workspace" }, "workspace": { "id": "...", "name": "Studio", "type": "business" }, "limits": { "requests_per_minute": 300, "active_keys": 25, "webhook_endpoints": 16, "sandboxes": 5 } } ``` ## 3. Find a Project and an account Posts belong to a Project and publish to its connected accounts. ```bash curl "https://api.nowscale.com/v1/projects" -H "Authorization: Bearer $NOWSCALE_API_KEY" curl "https://api.nowscale.com/v1/accounts?project_id=PROJECT_ID" -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ## 4. Schedule a post Send an `Idempotency-Key` with every create, so a retry never makes a second post. ```bash curl https://api.nowscale.com/v1/posts \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: launch-2026-12-01" \ -d '{ "project_id": "PROJECT_ID", "title": "We are live", "caption": "Our new collection is out today.", "media_ids": ["MEDIA_ID"], "scheduled_at": "2026-12-01T15:00:00Z", "targets": [{ "account_id": "ACCOUNT_ID" }] }' ``` The response is the post, with one target per account. Each target's `status` moves from `pending` to `published` (or `failed` with an `error_code`). Follow it with `GET /v1/posts/{post_id}` or, better, a [webhook](/webhooks). ## Next steps - [Upload media](/media) to get a `MEDIA_ID` for videos and images. - [Network settings](/posts#network-settings) for YouTube titles, TikTok privacy and more. - [Webhooks](/webhooks) to hear about published and failed posts. Source: https://docs.nowscale.dev/quickstart --- # Authentication > Workspace keys and personal keys, permissions, Project limits, IP rules and rotation. Send your key as a bearer token on every request: ```http Authorization: Bearer ns_live_4eC39HqLyjWDarjtT1zdp7dc0a1b2c ``` Keys start with `ns_live_`, then 38 letters and digits; the last six are a checksum, so a mistyped key is refused before anything else happens. A key that was revoked or expired answers `401` with `api_key_revoked` or `api_key_expired`. ## Two kinds of keys **Workspace keys** act for the Workspace. They keep working when people leave, and only the Workspace owner, admins and people with the Developer duty can create them. Creating or rolling a live one needs a passkey or an authenticator app; test keys in a sandbox only need a recent sign-in. **Personal keys** act as you, with exactly your access in the app: your Projects, your accounts and your role. When you leave the Workspace, your personal keys stop working. Business owners and admins can turn personal keys off for everyone. Either kind can only do what its permissions allow, and never more than the person who created it could do in the app. ## Permissions Choose a preset when you create a key, or pick permissions one by one. | Permission | Allows | Read only | Publishing | Community | Full access | | --- | --- | --- | --- | --- | --- | | `workspace:read` | Read Workspace details | Yes | | | Yes | | `projects:read` | Read Projects | Yes | | | Yes | | `projects:write` | Create and edit Projects | | | | Yes | | `accounts:read` | Read connected accounts | Yes | Yes | Yes | Yes | | `accounts:write` | Connect and disconnect accounts | | | | Yes | | `media:read` | Read media | Yes | Yes | | Yes | | `media:write` | Upload media | | Yes | | Yes | | `posts:read` | Read posts | Yes | Yes | Yes | Yes | | `posts:write` | Create and edit posts | | Yes | | Yes | | `posts:publish` | Schedule and publish posts | | Yes | | Yes | | `posts:delete` | Delete posts | | | | Yes | | `reviews:write` | Approve and reject posts | | | | Yes | | `comments:read` | Read comments | Yes | | Yes | Yes | | `comments:write` | Reply to comments | | | Yes | Yes | | `comments:moderate` | Hide and remove comments | | | Yes | Yes | | `analytics:read` | Read analytics | Yes | Yes | | Yes | | `content:read` | Read published content | Yes | Yes | Yes | Yes | | `ai:use` | Use AI tools (spends Tokens) | | | | Yes | | `usage:read` | Read API usage | Yes | | | Yes | | `events:read` | Read events | Yes | | | Yes | | `webhooks:read` | Read webhooks | Yes | | | Yes | | `webhooks:write` | Manage webhooks | | | | Yes | `webhooks:read` and `webhooks:write` are available on Workspace keys only. An endpoint that needs a permission the key lacks answers `403 insufficient_scope` and names it in `required_scope`. ## Limit a key to some Projects A key can be limited to some Projects. Anything in other Projects answers `404`, as if it did not exist. ## IP allowlists A key can accept requests only from the addresses you list (IPv4 or IPv6, single addresses or CIDR ranges). On Business, the owner can also set one rule for every key in the Workspace. Requests from other addresses answer `403 ip_not_allowed`. ## Rotate and revoke **Roll** a key to get a new secret. The old one can keep working for 1 hour, 24 hours or 7 days while you deploy the new one, or stop at once. **Revoke** stops a key immediately. Keys can also be created with an expiry date. If a key leaks, revoke it in Settings right away and create a new one. Source: https://docs.nowscale.dev/authentication --- # Use with AI tools > Markdown versions of every page, llms.txt, and instructions to give your coding agent. These docs are written to be read by people and by AI tools alike. - Add `.md` to any page address for its Markdown, for example [/webhooks.md](/webhooks.md). - Send `Accept: text/markdown` to any page address and you get the same Markdown. - [/llms.txt](/llms.txt) lists every page with a one-line summary. - [/llms-full.txt](/llms-full.txt) is the whole documentation, reference included, in one file. - [/openapi.json](/openapi.json) is the OpenAPI 3.1 description of every endpoint and event. ## Instructions for your coding agent Paste this into your project's `AGENTS.md`, `CLAUDE.md` or editor rules when you build against NOWScale: ```markdown ## NOWScale API - Docs: https://docs.nowscale.com/llms-full.txt (OpenAPI: https://docs.nowscale.com/openapi.json) - Base URL https://api.nowscale.com/v1, header `Authorization: Bearer $NOWSCALE_API_KEY`. Never put the key in client code. - Send `Idempotency-Key` on every POST that creates something; retry 429 after Retry-After and 5xx with backoff. - Errors are application/problem+json; branch on `code`. - Verify webhook signatures (Standard Webhooks, secret starts with whsec_) on the raw body before parsing. - Scheduling a TikTok target needs `privacy_level` chosen by the creator and `consent: true` after showing TikTok's consent text. ``` ## Keys for agents Give an agent its own key with only the permissions it needs, limited to the Projects it works on, and an expiry date. Keys without `posts:publish` can prepare posts but never schedule them without review. Source: https://docs.nowscale.dev/ai --- # Publish posts > Drafts and schedules, one post to many accounts, approvals, per-network settings and TikTok consent. A post is one piece of content with one or more **targets**: the accounts it publishes to. Each target has its own status, and can have its own time and settings. ## Create a draft ```bash curl https://api.nowscale.com/v1/posts \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 6f1c2e0a-2f6b-4b55-9d3f-0c6c1a6c9f11" \ -d '{ "project_id": "PROJECT_ID", "title": "Behind the scenes", "caption": "How we shot the new collection.", "media_ids": ["MEDIA_ID"], "targets": [{ "account_id": "YOUTUBE_ACCOUNT_ID" }, { "account_id": "INSTAGRAM_ACCOUNT_ID" }] }' ``` Without `scheduled_at` the post stays a draft. `media_ids` come from [media uploads](/media); the first item leads. `alt_text` sets alt text per media id. ## Schedule Add `scheduled_at` for every target, or set it per target: ```json { "project_id": "PROJECT_ID", "media_ids": ["MEDIA_ID"], "scheduled_at": "2026-12-01T15:00:00Z", "targets": [ { "account_id": "YOUTUBE_ACCOUNT_ID" }, { "account_id": "TIKTOK_ACCOUNT_ID", "scheduled_at": "2026-12-01T18:00:00Z" } ] } ``` Scheduling checks everything the app checks: each network's rules for the media, your plan's publication limit for that month, and the time. Schedules work in whole minutes, like the app's picker: a time before the start of the next minute (UTC) keeps the post as a draft when it has content. `draft: true` keeps a dated post as a draft. When a dated post is saved as a draft instead, the create or update response says why in `draft_reason`: `schedule_passed` for a time that came too soon, `publication_limit` when the plan's publication limit is reached, `project_restricted` for an archived Project or one over the plan, `network_locked`, `network_feature_locked` or `network_account_limit` when the plan does not include that network, feature or account, and `limit_reached` for any other limit. It is `null` when the post was saved as you asked, and it appears only in that response. To publish at once, create the post and call `POST /v1/posts/{post_id}/publish`. ## Approvals Posts follow the Project's approval policy, as in the app. A key without the `posts:publish` permission never schedules directly: its dated posts wait for review, whoever owns the key. With `posts:publish`, the policy decides (owners and admins never need review). Send a draft for review with `POST /v1/posts/{post_id}/review`, and decide with `/review/approve` or `/review/reject` (the `reviews:write` permission). Send `expected_revision` to make sure you approve the version you checked. ## Change, unschedule, delete - `PATCH /v1/posts/{post_id}` changes only the fields you send. `targets` and `media_ids` replace the whole list. - `POST /v1/posts/{post_id}/unschedule` moves a scheduled post back to drafts. - `POST /v1/posts/{post_id}/duplicate` copies a post into a new draft. - `DELETE /v1/posts/{post_id}` deletes it. Copies already on networks are removed where the network allows; the post shows `deleting` until that is done. ## Network settings `network_settings` applies settings to every target on a network; a target's `settings` override them for that account. Names are the composer's options in snake_case. For example: ```json { "network_settings": { "youtube": { "category_id": "22", "made_for_kids": false, "tags": ["tutorial"], "visibility": "public" }, "tiktok": { "privacy_level": "PUBLIC_TO_EVERYONE", "disable_comment": false, "consent": true }, "instagram": { "surface": "reel", "share_to_feed": true } } } ``` `GET /v1/networks` lists each network's rules: media kinds, file types and sizes, duration, visibility choices and placements. ## TikTok TikTok requires the creator to choose a privacy level from their own account's options and to consent before each post. When your product schedules TikTok posts: 1. Show the creator's TikTok account and let them pick `privacy_level` from the options TikTok gives that account. There is no default. 2. Show TikTok's consent text and only then send `"consent": true` in the TikTok settings (for the network or the target). A scheduled TikTok target without consent answers `400 tiktok_consent_required`. Integrators are bound by the TikTok terms in the API Terms. ## Follow a post Each target in `targets` has a `status` (`pending`, `uploading`, `uploaded`, `published`, `failed`), the network's `external_id` once known, and an `error_code` when it failed. The post's own `status` sums them up: `published`, `partial` or `failed` once every target is done. [Webhooks](/webhooks) tell you when that happens. Source: https://docs.nowscale.dev/posts --- # Upload media > Images and thumbnails in one PUT, videos in parts, then attach them to posts. Media goes straight to storage: the API gives you upload addresses, you send the bytes there, then you tell the API the upload is complete. Files count toward your plan's storage. ## Images and thumbnails ```bash curl https://api.nowscale.com/v1/media \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: upload-cover-42" \ -d '{ "project_id": "PROJECT_ID", "kind": "image", "content_type": "image/png", "size_bytes": 482113 }' ``` ```json { "object": "media_upload", "media_id": "MEDIA_ID", "method": "single", "upload_url": "https://...", "part_size": null, "part_count": null, "content_type": "image/png" } ``` Send the file to `upload_url` with a `PUT` and the same `Content-Type`, then complete it: ```bash curl -X PUT "$UPLOAD_URL" -H "Content-Type: image/png" --data-binary @cover.png curl -X POST https://api.nowscale.com/v1/media/MEDIA_ID/complete \ -H "Authorization: Bearer $NOWSCALE_API_KEY" -H "Content-Type: application/json" -d '{}' ``` ## Videos Videos upload in parts, so a large file can resume after a network error. 1. `POST /v1/media` with `"kind": "video"` returns `part_size` and `part_count`. 2. `POST /v1/media/{media_id}/parts` with `{ "part_numbers": [1, 2, 3] }` returns a short-lived address per part. 3. `PUT` each part (bytes `(n-1) × part_size` to `n × part_size`) and keep the `ETag` header of each response. 4. `POST /v1/media/{media_id}/complete` with every part and its ETag: ```json { "parts": [{ "part_number": 1, "etag": "\"a54357aff0632cce46d942af68356b38\"" }, { "part_number": 2, "etag": "\"0c78aef83f66abc1fa1e8477f296d394\"" }], "duration_seconds": 42.5, "width": 1080, "height": 1920 } ``` Duration and size are optional; each network checks the real file again at publish time. ## Limits `GET /v1/networks` lists, per network, the media kinds a post can carry, the accepted file types and the largest file. Your plan also has a per-file size and a storage total; an upload over them answers with codes such as `plan_limit_video_size`. Media is ready when its `status` is `uploaded`. Attach it to a post with `media_ids`. Source: https://docs.nowscale.dev/media --- # Answer comments > Read comment threads, reply and moderate, safely against changes on the network. Comments arrive in threads: a top comment with its replies, on one account's video or post. ## Read threads ```bash curl "https://api.nowscale.com/v1/comments/threads?status=needs_reply&limit=50" \ -H "Authorization: Bearer $NOWSCALE_API_KEY" curl "https://api.nowscale.com/v1/comments/threads/THREAD_ID" \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` A thread carries `conversation_version`, which changes whenever its conversation changes on the network. ## Reply ```bash curl https://api.nowscale.com/v1/comments/threads/THREAD_ID/replies \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: reply-THREAD_ID-1" \ -d '{ "text": "Thank you! The full tutorial is on our channel.", "expected_conversation_version": 7 }' ``` `expected_conversation_version` is the version you read. If the conversation changed in the meantime, the reply is not sent and the API answers `409 conversation_changed`: read the thread again and decide. `reply_to_comment_id` answers a specific comment instead of the top one. ## Moderate `POST /v1/comments/threads/{thread_id}/comments/{comment_id}/moderate` with an `action`: `approve`, `hide`, `unhide`, `reject`, `ban_author` or `delete`, where the network supports it. ## Who can reply Replies and moderation go out through the connected account, and today only the person who connected that account can act on its comments, from the app or with their own personal key. Other keys can read the thread but get `403 comment_custodian_required` when they reply. Networks cap comment actions per day; past that, the API answers `429 comment_limit_reached` until the next day. Source: https://docs.nowscale.dev/comments --- # Webhooks > Signed events sent to your server, how to verify them, retries and secret rotation. A webhook endpoint is an https address on your server. When something happens in your Workspace, NOWScale sends it a signed `POST` with the event. Add endpoints in **Settings > API keys > Webhooks**, or with a Workspace key through `POST /v1/webhook_endpoints`. Each endpoint chooses its events (or all of them), can be limited to some Projects, and has its own signing secret that starts with `whsec_`. The URL must be public, use https on port 443, and answer within 15 seconds. ## The request ```http POST /nowscale/webhooks HTTP/1.1 content-type: application/json webhook-id: msg_2Lg8wVwqgQfM6s3E9hJcKx1Y webhook-timestamp: 1764601200 webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4= { "id": "evt_hK4fUeZNPU0YyNcWPW12zrSa", "object": "event", "type": "post.published", "api_version": "2026-12-01", "created": 1764601199, "livemode": true, "workspace_id": "...", "project_id": "...", "data": { "object": { "object": "post", "id": "...", "status": "published", "published_at": "2026-12-01T15:00:04Z" } } } ``` Events carry a small snapshot. Fetch the full object from the API when you need more. Answer with any `2xx` quickly, then do the work. Use `webhook-id` to ignore duplicates: it is the same on every retry of the same delivery. ## Verify the signature The signature follows [Standard Webhooks](https://www.standardwebhooks.com): an HMAC-SHA256 of `id.timestamp.body` with your secret, base64 encoded. Always verify it on the **raw** body, before parsing the JSON, and refuse old timestamps. ```js import { createHmac, timingSafeEqual } from "node:crypto"; // secret: "whsec_...", headers: the three webhook-* headers, body: the raw request body as a string. export function verifyNowScaleWebhook(secret, headers, body, toleranceSeconds = 300) { const id = headers["webhook-id"]; const timestamp = Number(headers["webhook-timestamp"]); if (!id || !Number.isInteger(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false; const key = Buffer.from(secret.slice("whsec_".length), "base64"); const expected = createHmac("sha256", key).update(`${id}.${timestamp}.${body}`).digest(); return String(headers["webhook-signature"] ?? "").split(" ").some((entry) => { const [version, signature] = entry.split(","); if (version !== "v1" || !signature) return false; const given = Buffer.from(signature, "base64"); return given.length === expected.length && timingSafeEqual(given, expected); }); } ``` ```python import base64, hashlib, hmac, time def verify_nowscale_webhook(secret: str, headers: dict, body: str, tolerance: int = 300) -> bool: msg_id = headers.get("webhook-id", "") timestamp = int(headers.get("webhook-timestamp", "0")) if not msg_id or abs(time.time() - timestamp) > tolerance: return False key = base64.b64decode(secret.removeprefix("whsec_")) expected = hmac.new(key, f"{msg_id}.{timestamp}.{body}".encode(), hashlib.sha256).digest() for entry in headers.get("webhook-signature", "").split(" "): version, _, signature = entry.partition(",") if version == "v1" and hmac.compare_digest(base64.b64decode(signature), expected): return True return False ``` Any Standard Webhooks library works too. ## Events | Event | When | | --- | --- | | `post.created` | A post was created, in the app or through the API. | | `post.published` | Every target of a post is live on its network. | | `post.partially_published` | A post finished with some targets live and some failed. | | `post.failed` | A post finished with every target failed. | | `post.deleted` | A post was deleted from NOWScale. | | `webhook_endpoint.test` | Sent only by the test button or POST /v1/webhook_endpoints/{id}/test. | Post events carry the post's `id`, `project_id`, `status`, `scheduled_at` and `published_at`. ## Retries If your endpoint does not answer `2xx` within 15 seconds, the delivery is retried: After 1 minute, then 5 minutes, then 30 minutes, then 2 hours, then 6 hours, then 12 hours, then 24 hours, then 24 hours (each with up to 20% jitter): 9 attempts over about three days. A sandbox's endpoints retry after 1 minute, then 15 minutes, then 1.5 hours: 4 attempts over about two hours. - `410 Gone` turns the endpoint off at once. - An endpoint that keeps failing for 5 days is turned off. Turn it back on in Settings or with `PATCH` and `"enabled": true`. - `GET /v1/webhook_endpoints/{endpoint_id}/deliveries` lists recent deliveries with their outcome, and `POST /v1/webhook_deliveries/{delivery_id}/retry` sends one again now. - Events are also available for 30 days at `GET /v1/events`, so you can catch up after an outage. ## Test **Send test event** in Settings, or `POST /v1/webhook_endpoints/{endpoint_id}/test`, sends a `webhook_endpoint.test` event to that endpoint only. ## Rotate the secret Rolling a secret creates a new one. For the next 24 hours every delivery carries two signatures, one per secret, so you can deploy the new secret without dropping events (the verification above accepts either). In Settings, revealing or rolling a secret needs a passkey or an authenticator app. Source: https://docs.nowscale.dev/webhooks --- # Sandboxes and test mode > Test keys, test accounts, presets, magic values and test helpers, without reaching a network. A sandbox is a test copy of your Workspace. It runs on the same plan and limits, has its own Projects, posts, keys and webhook endpoints, and nothing in it reaches a network, charges money or sends email. Use one to build and test an integration before it touches real accounts. ## Create a sandbox Owners, admins and people with the Developer duty create sandboxes in **Settings > API keys > Sandboxes** of the live Workspace. Your plan sets how many you can have. **Open** switches the app into the sandbox: a Test mode banner stays on every page until you exit. Keys you create inside a sandbox are test keys. They start with `ns_test_` and reach only that sandbox. Its events carry `"livemode": false`, and its webhook endpoints retry for about two hours instead of three days. ## Test accounts A sandbox never connects a real network account. Add test accounts instead: - In the app: **Connections > Add connection** in the sandbox, then pick a network. The account appears at once. - With the API: `POST /v1/test_helpers/accounts` with a test key. ```bash curl https://api.nowscale.com/v1/test_helpers/accounts \ -H "Authorization: Bearer $NOWSCALE_TEST_KEY" \ -H "Idempotency-Key: 9b2f4c1e-7a3d-4e8b-a6c5-1d0e2f3a4b5c" \ -H "Content-Type: application/json" \ -d '{"project_id": "PROJECT_ID", "network": "youtube", "preset": "healthy"}' ``` Plan limits per network apply as for real accounts. A test account's preset decides how its network behaves: | Preset | Behaviour | | --- | --- | | `healthy` | Every post publishes on time. | | `reauth_required` | The account needs reconnecting: its posts fail with `reauth_required`. | | `expiring` | Publishes normally; its access is shown as expiring within three days. | | `slow` | Publishes 90 seconds after the due time. | | `flaky` | About 3 in 10 targets fail with `provider_timeout` (always the same ones). | Switch a preset with `POST /v1/test_helpers/accounts/{account_id}`, for example to `reauth_required` to see how your integration handles an account that must reconnect. ## Publishing in a sandbox Create, schedule and publish posts exactly as in live mode, in the app or with `POST /v1/posts`. When a target's time comes, the simulator plays the network: the target publishes (it gets an `external_id` starting with `sim_`) or fails with a code the live API also uses. The post's status, its events and your webhooks follow, as in live mode. Force an outcome with magic values in the post's caption: - `ns_test:fail=` fails every target of the post. - `ns_test:youtube.fail=` fails only the YouTube target (any network id works). - `ns_test:delay=` publishes that long after the due time. `ns_test:fail=` accepts `reauth_required`, `quota_exceeded`, `connection_gone`, `upload_rejected`, `provider_timeout`, `video_rejected`, `processing_failed`, `publish_outcome_unknown`, `tiktok_private_account_required`. `ns_test:delay=` holds a publish up to 3600 seconds. `POST /v1/test_helpers/posts/{post_id}/advance` runs the simulator for one post right away, delays included, and answers with the post. A target scheduled in the future stays scheduled; publish the post to send it now. ## What a sandbox does not do yet - Analytics, captions, playlists and other reads that ask the network live answer `simulated_account` for test accounts. - Comments on test accounts are not simulated yet. - Paid AI is simulated through the API (see [AI operations and Tokens](/ai-operations)); in the app, AI features other than comment reply drafts answer as unavailable. - Deleting a sandbox hides it at once; its content is removed later. Source: https://docs.nowscale.dev/sandboxes --- # AI operations and Tokens > Paid AI through the API, max_tokens, prices, per-key caps and what a sandbox does instead. Some endpoints run NOWScale's AI and spend the Workspace's Tokens, exactly as the same feature in the app does. Your key needs the `ai:use` permission. Today the API offers drafting a reply to a comment (`POST /v1/ai/comment_replies`); more operations follow. ## Every paid call states its limit Send `max_tokens`, the most the call may cost, in Tokens: ```bash curl https://api.nowscale.com/v1/ai/comment_replies \ -H "Authorization: Bearer $NOWSCALE_KEY" \ -H "Idempotency-Key: 3f2a9c4e-1b7d-4e6a-9c2f-8d1e0b4a7c55" \ -H "Content-Type: application/json" \ -d '{"comment": "What camera do you use?", "video_title": "My studio tour", "max_tokens": 1}' ``` - Without `max_tokens` the call is refused with `402 max_tokens_required` and nothing is charged. The problem's `quote` tells you the price: its `operation` and `tokens`, a decimal string. - When the price is higher than `max_tokens`, the call is refused with `402 max_tokens_exceeded` and the same `quote`. - Otherwise the operation runs at that price, and the response's `tokens` says what was charged. The price is fixed when the call starts: if it changes before the charge, the call is refused rather than charged more. Send an `Idempotency-Key` so a retry after a network error never charges twice. ## Caps per key When you create a key with the `ai:use` permission in **Settings > API keys**, you can give it a daily and a monthly Token cap. A call that would pass a cap is refused with `402 key_spend_cap_reached`, which names the cap and the window (`day` or `month`, counted in UTC). A call that fails gives its reservation back. The Workspace's own balance, plan allowance and Extra Usage settings still apply first: `402 insufficient_balance` or `402 overage_disabled` mean the Workspace, not the key, is out of Tokens. ## In a sandbox AI in a sandbox is simulated: the answer is a realistic placeholder marked `"simulated": true`, no model runs and nothing is charged (`tokens` is `"0"`). Prices, `max_tokens` and key caps behave exactly as in live mode, so you can test the whole flow. The sandbox's test balance only refuses a call it could not cover. Source: https://docs.nowscale.dev/ai-operations --- # Making requests > JSON in and out, request ids, versions, pagination and idempotency. ## JSON Send JSON bodies with `Content-Type: application/json` (1 MB at most). Unknown fields and unknown query parameters are refused with `400 invalid_request`, so a typo never passes silently. Times are ISO 8601 in UTC, such as `2026-12-01T15:00:00Z`. Ids are opaque strings; store them as they are. ## Request ids Every response carries a `Request-Id` header. Include it when you contact support; it finds the request in our logs. ## Versions Every response carries `NOWScale-Version`, the API version your key uses. Within a version, changes only add things: new endpoints, new optional fields, new event types and new error codes. Code that ignores unknown fields keeps working. See [Versioning](/versioning). ## Pagination List endpoints return pages, newest first unless the endpoint says otherwise: ```json { "object": "list", "data": [ ... ], "has_more": true, "next_cursor": "WyIyMDI2LTEx..." } ``` Ask for the next page with `?cursor=` and the `next_cursor` you received. `limit` takes 1 to 100 (25 by default). Cursors stay valid while new items arrive, so you never see an item twice. ```bash curl "https://api.nowscale.com/v1/posts?limit=100&cursor=WyIyMDI2LTEx..." \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ## Idempotency `POST` endpoints that create something accept an `Idempotency-Key` header: any string up to 255 visible characters, unique per operation (a UUID is a good choice). - The first request with a key runs, and its response is kept for 24 hours. - A retry with the same key and the same body returns that response again, with `Idempotent-Replayed: true`. Nothing runs twice. - The same key with a different body answers `422 idempotency_key_reused`. - A retry while the first request is still running answers `409 idempotency_in_flight`; retry after a second. - If the first request failed on our side (`5xx`), the key is free again and a retry runs normally. Keys belong to the API key that sent them. ## Errors Errors are [problem details](/errors) with a stable `code`. Source: https://docs.nowscale.dev/requests --- # Errors > Problem details, stable codes and what to do about each one. Errors use [RFC 9457 problem details](https://www.rfc-editor.org/rfc/rfc9457) with `Content-Type: application/problem+json`: ```json { "type": "about:blank", "title": "Key lacks a permission", "status": 403, "detail": "This key needs the posts:publish permission for this endpoint.", "code": "insufficient_scope", "request_id": "req_f3T0p9vK2xQm1aLs", "required_scope": "posts:publish" } ``` Branch on `code`, never on `title` or `detail`: codes are stable, the texts may improve. Validation errors list each problem in `errors`, with a `parameter` (query) or a `pointer` (body, such as `/targets/0/account_id`). ## Codes | Code | Status | Meaning | | --- | --- | --- | | `invalid_cursor` | 400 | Invalid cursor | | `invalid_idempotency_key` | 400 | Invalid Idempotency-Key | | `invalid_request` | 400 | Invalid request | | `invalid_webhook_url` | 400 | Invalid webhook URL | | `tiktok_consent_required` | 400 | TikTok consent required | | `api_key_expired` | 401 | API key expired | | `api_key_revoked` | 401 | API key revoked | | `invalid_api_key` | 401 | Invalid API key | | `missing_api_key` | 401 | API key required | | `key_spend_cap_reached` | 402 | Key Token cap reached | | `max_tokens_exceeded` | 402 | Price above max_tokens | | `max_tokens_required` | 402 | max_tokens required | | `plan_required` | 402 | Plan does not include API access | | `workspace_locked` | 402 | Workspace locked | | `comment_custodian_required` | 403 | Only the account's custodian can act on its comments | | `developer_platform_unavailable` | 403 | API access not enabled | | `insufficient_scope` | 403 | Key lacks a permission | | `ip_not_allowed` | 403 | IP address not allowed | | `network_locked` | 403 | Network not included in the plan | | `network_unavailable` | 403 | Network not available | | `permission_denied` | 403 | Not allowed | | `personal_keys_disabled` | 403 | Personal keys are turned off | | `test_key_required` | 403 | Test key required | | `workspace_key_required` | 403 | Workspace key required | | `workspace_unavailable` | 403 | Workspace unavailable | | `not_found` | 404 | Resource not found | | `unknown_endpoint` | 404 | Not found | | `method_not_allowed` | 405 | Method not allowed | | `account_limit_reached` | 409 | Account limit reached for this network | | `comment_draft_pending` | 409 | A reply draft is waiting | | `conversation_changed` | 409 | Conversation changed | | `idempotency_in_flight` | 409 | Request with this Idempotency-Key in progress | | `simulated_account` | 409 | Not available for test accounts | | `webhook_endpoint_limit_reached` | 409 | Webhook endpoint limit reached | | `idempotency_key_reused` | 422 | Idempotency-Key used for another request | | `project_restricted` | 423 | Project restricted | | `workspace_read_only` | 423 | Workspace is read-only | | `comment_limit_reached` | 429 | Daily comment limit reached | | `rate_limited` | 429 | Too many requests | | `server_error` | 500 | Server error | | `provider_error` | 502 | The network refused or failed | | `service_unavailable` | 503 | Service unavailable | Actions on posts, media and accounts can also answer the same codes as the app, such as `past_schedule`, `video_required`, `plan_limit_video_size` or `publication_limit` (a plan limit, with the details in `limits`). They keep the HTTP status that fits: `400` for a request to fix, `402` for the plan or balance, `409` for a conflict with the current state. ## Retrying Retry `429` after `Retry-After` seconds, and `500`, `502` and `503` with exponential backoff. Send the same `Idempotency-Key` so a retry never repeats a change. Do not retry other `4xx` errors without changing the request. A `500` carries a `reference`. Send it with the `Request-Id` when you contact support. Source: https://docs.nowscale.dev/errors --- # Rate limits > Requests per minute per plan, per key, and the headers that tell you where you stand. Every Workspace has a number of requests per minute shared by all its keys, and each key can have a lower limit of its own. Test traffic in sandboxes has its own pool. | Plan | Requests per minute | API keys | Webhook endpoints | Sandboxes | | --- | --- | --- | --- | --- | | Creator | 60 | 3 | 3 | 1 | | Pro | 120 | 10 | 10 | 2 | | Business | 300 | 25 | 16 | 5 | | Agency | 600 | 100 | 32 | 5 | | Enterprise | By contract | By contract | By contract | By contract | Free does not include the API. Most requests count once. Requests that do more work count five times: duplicating a post, and AI tools when they arrive. ## Headers Every response says where you stand: ```http RateLimit-Policy: "workspace";q=300;w=60, "key";q=300;w=60 RateLimit: "key";r=287;t=41 ``` `q` is the quota per window of `w` seconds, `r` what remains and `t` the seconds until the window resets. `RateLimit` reports whichever of the two limits is closer. When you go over, the API answers `429 rate_limited` with `Retry-After` in seconds. Wait that long, then retry. ## Plan limits Rate limits are separate from what your plan allows: how many posts you can publish, how much storage you have, how many accounts you can connect. The API uses exactly the same limits as the app, so a post the app would refuse is refused here too, with the same code. Source: https://docs.nowscale.dev/rate-limits --- # Versioning > Dated API versions, what can change within one, and how you hear about changes. The API has a path version (`/v1`) and a dated version, shown in the `NOWScale-Version` header of every response and in `api_version` on every event. The first dated version is `2026-12-01`. Within a dated version, changes only add: - new endpoints and new optional request fields, - new fields in responses and event payloads, - new event types (endpoints that listen to all events receive them), - new error codes and new values in fields documented as open lists. Write code that ignores fields it does not know and handles an unknown error `code` like a generic error of its status. A change that could break working code (removing or renaming a field, changing what a value means) comes as a new dated version. Your keys keep their version until you move them, and we announce new versions in the changelog well in advance. Source: https://docs.nowscale.dev/versioning --- # Changelog > What changed in the API, by version and date. The API is versioned by date. Within a version, changes only add: new endpoints, optional fields, response fields and events. Anything that could break an integration ships in a new version, listed here first. ## 2026-12-01 (first version, in beta) - Keys: Workspace and personal keys, permissions, Project limits, IP allowlists, rotation with overlap; test keys (`ns_test_`) for sandboxes. - Projects, accounts, networks, media uploads (single and multipart), posts (create, schedule, publish, update, duplicate, delete), reviews, comments (threads, replies, moderation), analytics summaries, usage. - Posts: a create or update that saves a dated post as a draft says why in `draft_reason`. - Events and webhook endpoints, signed with Standard Webhooks. - Sandboxes: test accounts, presets, magic caption values and test helpers. - AI: comment reply drafts, with `max_tokens` and per-key Token caps. Source: https://docs.nowscale.dev/changelog --- # Keys > 1 endpoint: retrieve the current key. - [Retrieve the current key](#retrieve-the-current-key): `GET /v1/me` ### Retrieve the current key `GET /v1/me` Returns the key making the request: its permissions, the Workspace it belongs to, who it acts as and the plan's limits. Any valid key #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"api_key"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `name` | string | Yes | | | `kind` | `workspace`, `personal` | Yes | | | `livemode` | boolean | Yes | | | `api_version` | string | Yes | | | `scopes` | array of string | Yes | | | `project_ids` | array of string | Yes | | | `acting_as` | object or object | Yes | | | `workspace` | object | Yes | | | `workspace.id` | string | Yes | (up to 128 characters) | | `workspace.name` | string | Yes | | | `workspace.type` | `business`, `personal` | Yes | | | `limits` | object | Yes | | | `limits.requests_per_minute` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `limits.active_keys` | integer or null | Yes | | | `limits.webhook_endpoints` | integer or null | Yes | | | `limits.sandboxes` | integer or null | Yes | | ```bash curl https://api.nowscale.com/v1/me \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/keys --- # Workspace > 1 endpoint: retrieve the workspace. - [Retrieve the Workspace](#retrieve-the-workspace): `GET /v1/workspace` ### Retrieve the Workspace `GET /v1/workspace` Returns the Workspace this key belongs to and its developer platform limits. Permission: `workspace:read` #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"workspace"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `name` | string | Yes | | | `type` | `business`, `personal` | Yes | | | `limits` | object | Yes | | | `limits.requests_per_minute` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `limits.active_keys` | integer or null | Yes | | | `limits.webhook_endpoints` | integer or null | Yes | | | `limits.sandboxes` | integer or null | Yes | | ```bash curl https://api.nowscale.com/v1/workspace \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/workspace --- # Projects > 2 endpoints: list projects, retrieve a project. - [List Projects](#list-projects): `GET /v1/projects` - [Retrieve a Project](#retrieve-a-project): `GET /v1/projects/{project_id}` ### List Projects `GET /v1/projects` Lists the Projects this key can reach, oldest first. A key limited to some Projects sees only those. Permission: `projects:read` #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | No | Items per page, 1 to 100. (1 to 100, default 25) | | `cursor` | string | No | The next_cursor of the previous page. (up to 512 characters) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"project"` | Yes | | | `data[].id` | string | Yes | (up to 128 characters) | | `data[].name` | string | Yes | | | `data[].status` | `active`, `archived` | Yes | | | `data[].timezone` | string | Yes | | | `data[].approval_policy` | `contributors`, `everyone` | Yes | | | `data[].created_at` | string | Yes | ISO 8601 date and time, UTC. | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/projects \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Retrieve a Project `GET /v1/projects/{project_id}` Returns one Project this key can reach. Permission: `projects:read` #### Path parameters | Name | Description | | --- | --- | | `project_id` | The project's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"project"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `name` | string | Yes | | | `status` | `active`, `archived` | Yes | | | `timezone` | string | Yes | | | `approval_policy` | `contributors`, `everyone` | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl https://api.nowscale.com/v1/projects/PROJECT_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/projects --- # Networks > 1 endpoint: list networks and their rules. - [List networks and their rules](#list-networks-and-their-rules): `GET /v1/networks` ### List networks and their rules `GET /v1/networks` Lists every network NOWScale publishes to, with the limits a post must respect there: media kinds, file types and sizes, duration, visibility choices, placements and how many media items one post may carry. Permission: `accounts:read` #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"network"` | Yes | | | `data[].id` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `data[].name` | string | Yes | | | `data[].visibilities` | array of string | Yes | | | `data[].max_media_items` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `data[].text_only_posts` | boolean | Yes | | | `data[].max_duration_seconds` | integer or null | Yes | | | `data[].media_kinds` | array of `video`, `image`, `text` | Yes | What one post may carry on this network. | | `data[].surfaces` | array of string | Yes | Placements a post can target, such as feed, reel or story. Empty when the network has one. | | `data[].video` | object or null | Yes | | | `data[].image` | object or null | Yes | | | `data[].thumbnail` | object or null | Yes | | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/networks \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/networks --- # Accounts > 2 endpoints: list connected accounts, retrieve an account. - [List connected accounts](#list-connected-accounts): `GET /v1/accounts` - [Retrieve an account](#retrieve-an-account): `GET /v1/accounts/{account_id}` ### List connected accounts `GET /v1/accounts` Lists the social accounts connected in the Projects this key can reach, oldest first. Permission: `accounts:read` #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | No | Items per page, 1 to 100. (1 to 100, default 25) | | `cursor` | string | No | The next_cursor of the previous page. (up to 512 characters) | | `project_id` | string | No | (up to 128 characters) | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | No | | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"account"` | Yes | | | `data[].id` | string | Yes | (up to 128 characters) | | `data[].project_id` | string or null | Yes | | | `data[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `data[].name` | string | Yes | | | `data[].handle` | string or null | Yes | | | `data[].avatar_url` | string or null | Yes | | | `data[].followers` | integer or null | Yes | | | `data[].status` | `active`, `reconnect_required`, `error` | Yes | | | `data[].connected_at` | string | Yes | ISO 8601 date and time, UTC. | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/accounts \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Retrieve an account `GET /v1/accounts/{account_id}` Returns one connected account. Permission: `accounts:read` #### Path parameters | Name | Description | | --- | --- | | `account_id` | The account's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"account"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `name` | string | Yes | | | `handle` | string or null | Yes | | | `avatar_url` | string or null | Yes | | | `followers` | integer or null | Yes | | | `status` | `active`, `reconnect_required`, `error` | Yes | | | `connected_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl https://api.nowscale.com/v1/accounts/ACCOUNT_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/accounts --- # Analytics > 2 endpoints: retrieve an account's analytics summary, retrieve a post's metrics. - [Retrieve an account's analytics summary](#retrieve-an-account-s-analytics-summary): `GET /v1/accounts/{account_id}/analytics` - [Retrieve a post's metrics](#retrieve-a-post-s-metrics): `GET /v1/posts/{post_id}/metrics` ### Retrieve an account's analytics summary `GET /v1/accounts/{account_id}/analytics` Reads the account's current counters from the network. YouTube and Instagram answer for the chosen range; TikTok and Facebook answer lifetime counters. Permission: `analytics:read` · Counts 5 requests #### Path parameters | Name | Description | | --- | --- | | `account_id` | The account's id. | #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `range` | `7`, `28`, `90` | No | Days, for networks that report a range. (default "28") | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"account_analytics"` | Yes | | | `account_id` | string | Yes | (up to 128 characters) | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `range_days` | integer or null | Yes | | | `data` | object | Yes | | ```bash curl https://api.nowscale.com/v1/accounts/ACCOUNT_ID/analytics \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Retrieve a post's metrics `GET /v1/posts/{post_id}/metrics` Reads lifetime views, likes, comments and shares of each published copy from its network. A copy the network cannot report returns nulls. Permission: `analytics:read` · Counts 5 requests #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"post_metrics"` | Yes | | | `data[].target_id` | string | Yes | (up to 128 characters) | | `data[].account_id` | string | Yes | (up to 128 characters) | | `data[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `data[].external_id` | string | Yes | | | `data[].views` | number or null | Yes | | | `data[].likes` | number or null | Yes | | | `data[].comments` | number or null | Yes | | | `data[].shares` | number or null | Yes | | | `data[].url` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/posts/POST_ID/metrics \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/analytics --- # Posts > 8 endpoints: list posts, retrieve a post, create a post, update a post, unschedule a post, publish a post now, duplicate a post, delete a post. - [List posts](#list-posts): `GET /v1/posts` - [Retrieve a post](#retrieve-a-post): `GET /v1/posts/{post_id}` - [Create a post](#create-a-post): `POST /v1/posts` - [Update a post](#update-a-post): `PATCH /v1/posts/{post_id}` - [Unschedule a post](#unschedule-a-post): `POST /v1/posts/{post_id}/unschedule` - [Publish a post now](#publish-a-post-now): `POST /v1/posts/{post_id}/publish` - [Duplicate a post](#duplicate-a-post): `POST /v1/posts/{post_id}/duplicate` - [Delete a post](#delete-a-post): `DELETE /v1/posts/{post_id}` ### List posts `GET /v1/posts` Lists posts, newest first, with each network target's status. Permission: `posts:read` #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | No | Items per page, 1 to 100. (1 to 100, default 25) | | `cursor` | string | No | The next_cursor of the previous page. (up to 512 characters) | | `project_id` | string | No | (up to 128 characters) | | `status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed` | No | | | `scheduled_from` | string | No | Only posts scheduled at or after this time (ISO 8601). | | `scheduled_to` | string | No | Only posts scheduled before this time (ISO 8601). | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"post"` | Yes | | | `data[].id` | string | Yes | (up to 128 characters) | | `data[].project_id` | string or null | Yes | | | `data[].status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed`, `deleting` | Yes | | | `data[].review_status` | `not_required`, `draft`, `in_review`, `approved`, `rejected` | Yes | | | `data[].title` | string or null | Yes | | | `data[].caption` | string or null | Yes | | | `data[].visibility` | string | Yes | | | `data[].scheduled_at` | string or null | Yes | | | `data[].published_at` | string or null | Yes | | | `data[].created_at` | string | Yes | ISO 8601 date and time, UTC. | | `data[].updated_at` | string | Yes | ISO 8601 date and time, UTC. | | `data[].targets` | array of object | Yes | | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/posts \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Retrieve a post `GET /v1/posts/{post_id}` Returns one post with each network target's status. Permission: `posts:read` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed`, `deleting` | Yes | | | `review_status` | `not_required`, `draft`, `in_review`, `approved`, `rejected` | Yes | | | `title` | string or null | Yes | | | `caption` | string or null | Yes | | | `visibility` | string | Yes | | | `scheduled_at` | string or null | Yes | | | `published_at` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `updated_at` | string | Yes | ISO 8601 date and time, UTC. | | `targets` | array of object | Yes | | | `targets[].object` | `"post_target"` | Yes | | | `targets[].id` | string | Yes | (up to 128 characters) | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `targets[].status` | `pending`, `uploading`, `uploaded`, `published`, `failed` | Yes | | | `targets[].stage` | string | Yes | | | `targets[].external_id` | string or null | Yes | | | `targets[].error_code` | string or null | Yes | | | `targets[].scheduled_at` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/posts/POST_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Create a post `POST /v1/posts` Creates a draft or a scheduled post in a Project. Scheduling needs the posts:publish permission and follows the Project's approval policy: without it, or when the policy asks, the post waits for review. Send an Idempotency-Key so a retry never creates a second post. Permission: `posts:write` · Accepts `Idempotency-Key` · Answers `201` #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `project_id` | string | Yes | (up to 128 characters) | | `title` | string | No | (up to 150 characters) | | `caption` | string | No | (up to 5000 characters) | | `media_ids` | array of string | No | Ordered media from POST /v1/media. The first item leads. | | `alt_text` | object | No | Alt text by media id. | | `visibility` | `public`, `unlisted`, `private` | No | | | `targets` | array of object | No | The accounts this post publishes to. | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].scheduled_at` | string or null | No | When this account publishes. Defaults to the post's scheduled_at. | | `targets[].settings` | object | No | This account's own settings, overriding network_settings for its network. | | `scheduled_at` | string or null | No | When every target without its own scheduled_at publishes. Null or absent keeps a draft. | | `draft` | boolean | No | Keep the post as a draft even when it has a schedule. | | `network_settings` | object | No | Settings per network, applied to every target on that network. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed`, `deleting` | Yes | | | `review_status` | `not_required`, `draft`, `in_review`, `approved`, `rejected` | Yes | | | `title` | string or null | Yes | | | `caption` | string or null | Yes | | | `visibility` | string | Yes | | | `scheduled_at` | string or null | Yes | | | `published_at` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `updated_at` | string | Yes | ISO 8601 date and time, UTC. | | `targets` | array of object | Yes | | | `targets[].object` | `"post_target"` | Yes | | | `targets[].id` | string | Yes | (up to 128 characters) | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `targets[].status` | `pending`, `uploading`, `uploaded`, `published`, `failed` | Yes | | | `targets[].stage` | string | Yes | | | `targets[].external_id` | string or null | Yes | | | `targets[].error_code` | string or null | Yes | | | `targets[].scheduled_at` | string or null | Yes | | | `draft_reason` | `schedule_passed`, `publication_limit`, `project_restricted`, `network_locked`, `network_feature_locked`, `network_account_limit`, `limit_reached` or null | Yes | null when the post was saved as asked. Otherwise why a dated post was saved as a draft: schedule_passed (the time was before the start of the next minute), publication_limit (the plan's publication limit), project_restricted (the Project is archived or over the plan), network_locked, network_feature_locked or network_account_limit (the plan does not include that network, feature or account), limit_reached (another limit). Only in this response. | ```bash curl -X POST https://api.nowscale.com/v1/posts \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Update a post `PATCH /v1/posts/{post_id}` Changes a post's text, media, targets, schedule or network settings. Fields you leave out keep their value; targets and media_ids replace the whole list. Permission: `posts:write` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `title` | string | No | (up to 150 characters) | | `caption` | string | No | (up to 5000 characters) | | `media_ids` | array of string | No | Ordered media from POST /v1/media. The first item leads. | | `alt_text` | object | No | Alt text by media id. | | `visibility` | `public`, `unlisted`, `private` | No | | | `targets` | array of object | No | The accounts this post publishes to. | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].scheduled_at` | string or null | No | When this account publishes. Defaults to the post's scheduled_at. | | `targets[].settings` | object | No | This account's own settings, overriding network_settings for its network. | | `scheduled_at` | string or null | No | When every target without its own scheduled_at publishes. Null or absent keeps a draft. | | `draft` | boolean | No | Keep the post as a draft even when it has a schedule. | | `network_settings` | object | No | Settings per network, applied to every target on that network. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed`, `deleting` | Yes | | | `review_status` | `not_required`, `draft`, `in_review`, `approved`, `rejected` | Yes | | | `title` | string or null | Yes | | | `caption` | string or null | Yes | | | `visibility` | string | Yes | | | `scheduled_at` | string or null | Yes | | | `published_at` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `updated_at` | string | Yes | ISO 8601 date and time, UTC. | | `targets` | array of object | Yes | | | `targets[].object` | `"post_target"` | Yes | | | `targets[].id` | string | Yes | (up to 128 characters) | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `targets[].status` | `pending`, `uploading`, `uploaded`, `published`, `failed` | Yes | | | `targets[].stage` | string | Yes | | | `targets[].external_id` | string or null | Yes | | | `targets[].error_code` | string or null | Yes | | | `targets[].scheduled_at` | string or null | Yes | | | `draft_reason` | `schedule_passed`, `publication_limit`, `project_restricted`, `network_locked`, `network_feature_locked`, `network_account_limit`, `limit_reached` or null | Yes | null when the post was saved as asked. Otherwise why a dated post was saved as a draft: schedule_passed (the time was before the start of the next minute), publication_limit (the plan's publication limit), project_restricted (the Project is archived or over the plan), network_locked, network_feature_locked or network_account_limit (the plan does not include that network, feature or account), limit_reached (another limit). Only in this response. | ```bash curl -X PATCH https://api.nowscale.com/v1/posts/POST_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' ``` ### Unschedule a post `POST /v1/posts/{post_id}/unschedule` Moves a scheduled post back to drafts. Targets already published stay published. Permission: `posts:write` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed`, `deleting` | Yes | | | `review_status` | `not_required`, `draft`, `in_review`, `approved`, `rejected` | Yes | | | `title` | string or null | Yes | | | `caption` | string or null | Yes | | | `visibility` | string | Yes | | | `scheduled_at` | string or null | Yes | | | `published_at` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `updated_at` | string | Yes | ISO 8601 date and time, UTC. | | `targets` | array of object | Yes | | | `targets[].object` | `"post_target"` | Yes | | | `targets[].id` | string | Yes | (up to 128 characters) | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `targets[].status` | `pending`, `uploading`, `uploaded`, `published`, `failed` | Yes | | | `targets[].stage` | string | Yes | | | `targets[].external_id` | string or null | Yes | | | `targets[].error_code` | string or null | Yes | | | `targets[].scheduled_at` | string or null | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/posts/POST_ID/unschedule \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' ``` ### Publish a post now `POST /v1/posts/{post_id}/publish` Starts publishing every pending target now. The post's status reports the outcome as each network answers. Permission: `posts:publish` · Accepts `Idempotency-Key` · Answers `202` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed`, `deleting` | Yes | | | `review_status` | `not_required`, `draft`, `in_review`, `approved`, `rejected` | Yes | | | `title` | string or null | Yes | | | `caption` | string or null | Yes | | | `visibility` | string | Yes | | | `scheduled_at` | string or null | Yes | | | `published_at` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `updated_at` | string | Yes | ISO 8601 date and time, UTC. | | `targets` | array of object | Yes | | | `targets[].object` | `"post_target"` | Yes | | | `targets[].id` | string | Yes | (up to 128 characters) | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `targets[].status` | `pending`, `uploading`, `uploaded`, `published`, `failed` | Yes | | | `targets[].stage` | string | Yes | | | `targets[].external_id` | string or null | Yes | | | `targets[].error_code` | string or null | Yes | | | `targets[].scheduled_at` | string or null | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/posts/POST_ID/publish \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Duplicate a post `POST /v1/posts/{post_id}/duplicate` Copies a post, its media and targets into a new draft in the same Project. Permission: `posts:write` · Counts 5 requests · Accepts `Idempotency-Key` · Answers `201` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed`, `deleting` | Yes | | | `review_status` | `not_required`, `draft`, `in_review`, `approved`, `rejected` | Yes | | | `title` | string or null | Yes | | | `caption` | string or null | Yes | | | `visibility` | string | Yes | | | `scheduled_at` | string or null | Yes | | | `published_at` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `updated_at` | string | Yes | ISO 8601 date and time, UTC. | | `targets` | array of object | Yes | | | `targets[].object` | `"post_target"` | Yes | | | `targets[].id` | string | Yes | (up to 128 characters) | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `targets[].status` | `pending`, `uploading`, `uploaded`, `published`, `failed` | Yes | | | `targets[].stage` | string | Yes | | | `targets[].external_id` | string or null | Yes | | | `targets[].error_code` | string or null | Yes | | | `targets[].scheduled_at` | string or null | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/posts/POST_ID/duplicate \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Delete a post `DELETE /v1/posts/{post_id}` Deletes a post. Copies NOWScale already placed on networks are removed where the network allows it; the post shows status deleting until that finishes. Permission: `posts:delete` · Answers `202` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `status` | `"deleting"` | Yes | | ```bash curl -X DELETE https://api.nowscale.com/v1/posts/POST_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/posts --- # Media > 5 endpoints: list media, retrieve media, start a media upload, sign video upload parts, complete a media upload. - [List media](#list-media): `GET /v1/media` - [Retrieve media](#retrieve-media): `GET /v1/media/{media_id}` - [Start a media upload](#start-a-media-upload): `POST /v1/media` - [Sign video upload parts](#sign-video-upload-parts): `POST /v1/media/{media_id}/parts` - [Complete a media upload](#complete-a-media-upload): `POST /v1/media/{media_id}/complete` ### List media `GET /v1/media` Lists uploaded videos, images and thumbnails, newest first. Permission: `media:read` #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | No | Items per page, 1 to 100. (1 to 100, default 25) | | `cursor` | string | No | The next_cursor of the previous page. (up to 512 characters) | | `project_id` | string | No | (up to 128 characters) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"media"` | Yes | | | `data[].id` | string | Yes | (up to 128 characters) | | `data[].project_id` | string or null | Yes | | | `data[].kind` | `video`, `image`, `thumbnail` | Yes | | | `data[].status` | string | Yes | | | `data[].content_type` | string | Yes | | | `data[].size_bytes` | integer or null | Yes | | | `data[].created_at` | string | Yes | ISO 8601 date and time, UTC. | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/media \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Retrieve media `GET /v1/media/{media_id}` Returns one uploaded file. Permission: `media:read` #### Path parameters | Name | Description | | --- | --- | | `media_id` | The media's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"media"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `kind` | `video`, `image`, `thumbnail` | Yes | | | `status` | string | Yes | | | `content_type` | string | Yes | | | `size_bytes` | integer or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl https://api.nowscale.com/v1/media/MEDIA_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Start a media upload `POST /v1/media` Reserves a media item in a Project and returns where to send the file. Images and thumbnails take one PUT to upload_url; videos upload in parts (sign them with the parts endpoint). Finish with the complete endpoint. Permission: `media:write` · Accepts `Idempotency-Key` · Answers `201` #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `project_id` | string | Yes | (up to 128 characters) | | `kind` | `video`, `image`, `thumbnail` | Yes | | | `content_type` | string | Yes | (up to 127 characters) | | `size_bytes` | integer | Yes | (at most 9007199254740991) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"media_upload"` | Yes | | | `media_id` | string | Yes | (up to 128 characters) | | `method` | `single`, `multipart` | Yes | | | `upload_url` | string or null | Yes | single: PUT the file here with its Content-Type. | | `part_size` | integer or null | Yes | multipart: bytes per part (the last part may be smaller). | | `part_count` | integer or null | Yes | | | `content_type` | string | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/media \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Sign video upload parts `POST /v1/media/{media_id}/parts` Returns a short-lived PUT URL for each part number of a video upload. Keep the ETag header of each part's response for the complete endpoint. Permission: `media:write` #### Path parameters | Name | Description | | --- | --- | | `media_id` | The media's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `part_numbers` | array of integer | Yes | | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"media_part_urls"` | Yes | | | `media_id` | string | Yes | (up to 128 characters) | | `urls` | object | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/media/MEDIA_ID/parts \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' ``` ### Complete a media upload `POST /v1/media/{media_id}/complete` Checks the uploaded file and makes it ready to attach to posts. Videos list every part with its ETag. Duration and size you report are checked again by each network at publish time. Permission: `media:write` · Accepts `Idempotency-Key` #### Path parameters | Name | Description | | --- | --- | | `media_id` | The media's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `parts` | array of object | No | | | `parts[].part_number` | integer | Yes | (1 to 10000) | | `parts[].etag` | string | Yes | (up to 256 characters) | | `duration_seconds` | number | No | (at most 172800) | | `width` | integer | No | (at most 16384) | | `height` | integer | No | (at most 16384) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"media"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `kind` | `video`, `image`, `thumbnail` | Yes | | | `status` | string | Yes | | | `content_type` | string | Yes | | | `size_bytes` | integer or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl -X POST https://api.nowscale.com/v1/media/MEDIA_ID/complete \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` Source: https://docs.nowscale.dev/reference/media --- # Comments > 4 endpoints: list comment threads, retrieve a comment thread, reply to a comment thread, moderate a comment. - [List comment threads](#list-comment-threads): `GET /v1/comments/threads` - [Retrieve a comment thread](#retrieve-a-comment-thread): `GET /v1/comments/threads/{thread_id}` - [Reply to a comment thread](#reply-to-a-comment-thread): `POST /v1/comments/threads/{thread_id}/replies` - [Moderate a comment](#moderate-a-comment): `POST /v1/comments/threads/{thread_id}/comments/{comment_id}/moderate` ### List comment threads `GET /v1/comments/threads` Lists comment conversations on the accounts this key can reach, most recent activity first. Permission: `comments:read` #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | No | Items per page, 1 to 100. (1 to 100, default 25) | | `cursor` | string | No | The next_cursor of the previous page. (up to 512 characters) | | `account_id` | string | No | (up to 128 characters) | | `status` | `needs_reply`, `completed`, `manual`, `sending`, `failed` | No | | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"comment_thread"` | Yes | | | `data[].id` | string | Yes | (up to 128 characters) | | `data[].account_id` | string | Yes | (up to 128 characters) | | `data[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `data[].status` | `needs_reply`, `completed`, `manual`, `sending`, `failed` | Yes | | | `data[].video_id` | string or null | Yes | | | `data[].video_title` | string or null | Yes | | | `data[].conversation_version` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `data[].latest_activity_at` | string | Yes | ISO 8601 date and time, UTC. | | `data[].messages` | array of object | No | | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/comments/threads \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Retrieve a comment thread `GET /v1/comments/threads/{thread_id}` Returns one conversation with its messages, oldest first. Permission: `comments:read` #### Path parameters | Name | Description | | --- | --- | | `thread_id` | The thread's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"comment_thread"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `account_id` | string | Yes | (up to 128 characters) | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `status` | `needs_reply`, `completed`, `manual`, `sending`, `failed` | Yes | | | `video_id` | string or null | Yes | | | `video_title` | string or null | Yes | | | `conversation_version` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `latest_activity_at` | string | Yes | ISO 8601 date and time, UTC. | | `messages` | array of object | No | | | `messages[].object` | `"comment"` | Yes | | | `messages[].id` | string | Yes | (up to 128 characters) | | `messages[].author_role` | string | Yes | | | `messages[].author_name` | string | Yes | | | `messages[].text` | string or null | Yes | | | `messages[].moderation_status` | string | Yes | | | `messages[].url` | string or null | Yes | | | `messages[].published_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl https://api.nowscale.com/v1/comments/threads/THREAD_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Reply to a comment thread `POST /v1/comments/threads/{thread_id}/replies` Posts a reply on the network, under the thread's top comment or the comment you name. Send the thread's conversation_version so a reply never lands on a conversation that changed since you read it. Replies go out as the account's owner; today only the person who connected the account can reply through it. Permission: `comments:write` · Accepts `Idempotency-Key` · Answers `201` #### Path parameters | Name | Description | | --- | --- | | `thread_id` | The thread's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `text` | string | Yes | (up to 10000 characters) | | `expected_conversation_version` | integer | Yes | (0 to 9007199254740991) | | `reply_to_comment_id` | string | No | The comment to answer. Defaults to the thread's top comment. (up to 128 characters) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"comment_thread"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `account_id` | string | Yes | (up to 128 characters) | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `status` | `needs_reply`, `completed`, `manual`, `sending`, `failed` | Yes | | | `video_id` | string or null | Yes | | | `video_title` | string or null | Yes | | | `conversation_version` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `latest_activity_at` | string | Yes | ISO 8601 date and time, UTC. | | `messages` | array of object | No | | | `messages[].object` | `"comment"` | Yes | | | `messages[].id` | string | Yes | (up to 128 characters) | | `messages[].author_role` | string | Yes | | | `messages[].author_name` | string | Yes | | | `messages[].text` | string or null | Yes | | | `messages[].moderation_status` | string | Yes | | | `messages[].url` | string or null | Yes | | | `messages[].published_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl -X POST https://api.nowscale.com/v1/comments/threads/THREAD_ID/replies \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Moderate a comment `POST /v1/comments/threads/{thread_id}/comments/{comment_id}/moderate` Approves, hides, unhides, rejects or deletes a comment, or bans its author, where the network supports the action. Send the thread's conversation_version. Permission: `comments:moderate` · Accepts `Idempotency-Key` #### Path parameters | Name | Description | | --- | --- | | `thread_id` | The thread's id. | | `comment_id` | The comment's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `action` | `approve`, `hide`, `unhide`, `reject`, `ban_author`, `delete` | Yes | | | `expected_conversation_version` | integer | Yes | (0 to 9007199254740991) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"comment_thread"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `account_id` | string | Yes | (up to 128 characters) | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `status` | `needs_reply`, `completed`, `manual`, `sending`, `failed` | Yes | | | `video_id` | string or null | Yes | | | `video_title` | string or null | Yes | | | `conversation_version` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `latest_activity_at` | string | Yes | ISO 8601 date and time, UTC. | | `messages` | array of object | No | | | `messages[].object` | `"comment"` | Yes | | | `messages[].id` | string | Yes | (up to 128 characters) | | `messages[].author_role` | string | Yes | | | `messages[].author_name` | string | Yes | | | `messages[].text` | string or null | Yes | | | `messages[].moderation_status` | string | Yes | | | `messages[].url` | string or null | Yes | | | `messages[].published_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl -X POST https://api.nowscale.com/v1/comments/threads/THREAD_ID/comments/COMMENT_ID/moderate \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` Source: https://docs.nowscale.dev/reference/comments --- # Reviews > 3 endpoints: send a post for review, approve a post, reject a post. - [Send a post for review](#send-a-post-for-review): `POST /v1/posts/{post_id}/review` - [Approve a post](#approve-a-post): `POST /v1/posts/{post_id}/review/approve` - [Reject a post](#reject-a-post): `POST /v1/posts/{post_id}/review/reject` ### Send a post for review `POST /v1/posts/{post_id}/review` Asks the Project's reviewers to approve a draft. Optionally assign a person or a Team. Permission: `posts:write` · Accepts `Idempotency-Key` · Answers `201` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `assigned_user_id` | string | No | (up to 128 characters) | | `assigned_team_id` | string | No | (up to 128 characters) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post_review"` | Yes | | | `post_id` | string | Yes | (up to 128 characters) | | `review_status` | `in_review`, `approved`, `rejected` | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/posts/POST_ID/review \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Approve a post `POST /v1/posts/{post_id}/review/approve` Approves the post's open review. Send expected_revision from the review to make sure you approve the version you checked. Permission: `reviews:write` · Accepts `Idempotency-Key` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `expected_revision` | integer | No | (0 to 9007199254740991) | | `reason` | string | No | (up to 2000 characters) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post_review"` | Yes | | | `post_id` | string | Yes | (up to 128 characters) | | `review_status` | `in_review`, `approved`, `rejected` | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/posts/POST_ID/review/approve \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Reject a post `POST /v1/posts/{post_id}/review/reject` Rejects the post's open review with an optional reason for its author. Permission: `reviews:write` · Accepts `Idempotency-Key` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `expected_revision` | integer | No | (0 to 9007199254740991) | | `reason` | string | No | (up to 2000 characters) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post_review"` | Yes | | | `post_id` | string | Yes | (up to 128 characters) | | `review_status` | `in_review`, `approved`, `rejected` | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/posts/POST_ID/review/reject \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` Source: https://docs.nowscale.dev/reference/reviews --- # Events > 2 endpoints: list events, retrieve an event. - [List events](#list-events): `GET /v1/events` - [Retrieve an event](#retrieve-an-event): `GET /v1/events/{event_id}` ### List events `GET /v1/events` Lists this Workspace's events of the last 30 days, newest first. The same objects your webhook endpoints receive. Permission: `events:read` #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | No | Items per page, 1 to 100. (1 to 100, default 25) | | `cursor` | string | No | The next_cursor of the previous page. (up to 512 characters) | | `type` | string | No | (up to 64 characters) | | `project_id` | string | No | (up to 128 characters) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].id` | string | Yes | (up to 128 characters) | | `data[].object` | `"event"` | Yes | | | `data[].type` | string | Yes | | | `data[].api_version` | string | Yes | | | `data[].created` | integer | Yes | Seconds since the epoch. (-9007199254740991 to 9007199254740991) | | `data[].livemode` | boolean | Yes | | | `data[].workspace_id` | string | Yes | (up to 128 characters) | | `data[].project_id` | string or null | Yes | | | `data[].data` | object | Yes | | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/events \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Retrieve an event `GET /v1/events/{event_id}` Returns one event of the last 30 days. Permission: `events:read` #### Path parameters | Name | Description | | --- | --- | | `event_id` | The event's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | Yes | (up to 128 characters) | | `object` | `"event"` | Yes | | | `type` | string | Yes | | | `api_version` | string | Yes | | | `created` | integer | Yes | Seconds since the epoch. (-9007199254740991 to 9007199254740991) | | `livemode` | boolean | Yes | | | `workspace_id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `data` | object | Yes | | | `data.object` | object | Yes | | ```bash curl https://api.nowscale.com/v1/events/EVENT_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/events --- # Webhooks > 9 endpoints: list webhook endpoints, create a webhook endpoint, retrieve a webhook endpoint, update a webhook endpoint, delete a webhook endpoint, roll a webhook secret, send a test event, list deliveries, retry a delivery. - [List webhook endpoints](#list-webhook-endpoints): `GET /v1/webhook_endpoints` - [Create a webhook endpoint](#create-a-webhook-endpoint): `POST /v1/webhook_endpoints` - [Retrieve a webhook endpoint](#retrieve-a-webhook-endpoint): `GET /v1/webhook_endpoints/{endpoint_id}` - [Update a webhook endpoint](#update-a-webhook-endpoint): `PATCH /v1/webhook_endpoints/{endpoint_id}` - [Delete a webhook endpoint](#delete-a-webhook-endpoint): `DELETE /v1/webhook_endpoints/{endpoint_id}` - [Roll a webhook secret](#roll-a-webhook-secret): `POST /v1/webhook_endpoints/{endpoint_id}/roll_secret` - [Send a test event](#send-a-test-event): `POST /v1/webhook_endpoints/{endpoint_id}/test` - [List deliveries](#list-deliveries): `GET /v1/webhook_endpoints/{endpoint_id}/deliveries` - [Retry a delivery](#retry-a-delivery): `POST /v1/webhook_deliveries/{delivery_id}/retry` ### List webhook endpoints `GET /v1/webhook_endpoints` Lists the Workspace's webhook endpoints. Secrets are never listed. Permission: `webhooks:read` #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"webhook_endpoint"` | Yes | | | `data[].id` | string | Yes | (up to 128 characters) | | `data[].url` | string | Yes | | | `data[].description` | string or null | Yes | | | `data[].enabled_events` | array of string | Yes | | | `data[].project_ids` | array of string | Yes | | | `data[].api_version` | string | Yes | | | `data[].include_comment_content` | boolean | Yes | | | `data[].status` | `enabled`, `disabled` | Yes | | | `data[].disabled_reason` | string or null | Yes | manual, gone (the endpoint answered 410) or failing (five days of failures). | | `data[].failing_since` | string or null | Yes | | | `data[].secret_last4` | string or null | Yes | | | `data[].created_at` | string | Yes | ISO 8601 date and time, UTC. | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/webhook_endpoints \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Create a webhook endpoint `POST /v1/webhook_endpoints` Adds an https endpoint (port 443) that receives the events you choose, signed with its own whsec_ secret. The secret is in this response only; reveal it later in Settings. Permission: `webhooks:write` · Accepts `Idempotency-Key` · Answers `201` #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | Yes | (up to 2048 characters) | | `description` | string | No | (up to 200 characters) | | `enabled_events` | array of `*`, `post.created`, `post.published`, `post.partially_published`, `post.failed`, `post.deleted` | Yes | Event types to receive, or ["*"] for all. | | `project_ids` | array of string | No | Only events from these Projects. Empty or absent: every Project. | | `include_comment_content` | boolean | No | Comment events carry the comment's text and author name. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"webhook_endpoint"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `url` | string | Yes | | | `description` | string or null | Yes | | | `enabled_events` | array of string | Yes | | | `project_ids` | array of string | Yes | | | `api_version` | string | Yes | | | `include_comment_content` | boolean | Yes | | | `status` | `enabled`, `disabled` | Yes | | | `disabled_reason` | string or null | Yes | manual, gone (the endpoint answered 410) or failing (five days of failures). | | `failing_since` | string or null | Yes | | | `secret_last4` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `secret` | string | Yes | The signing secret, shown once. | ```bash curl -X POST https://api.nowscale.com/v1/webhook_endpoints \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Retrieve a webhook endpoint `GET /v1/webhook_endpoints/{endpoint_id}` Returns one endpoint, with the last four characters of its secret. Permission: `webhooks:read` #### Path parameters | Name | Description | | --- | --- | | `endpoint_id` | The endpoint's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"webhook_endpoint"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `url` | string | Yes | | | `description` | string or null | Yes | | | `enabled_events` | array of string | Yes | | | `project_ids` | array of string | Yes | | | `api_version` | string | Yes | | | `include_comment_content` | boolean | Yes | | | `status` | `enabled`, `disabled` | Yes | | | `disabled_reason` | string or null | Yes | manual, gone (the endpoint answered 410) or failing (five days of failures). | | `failing_since` | string or null | Yes | | | `secret_last4` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl https://api.nowscale.com/v1/webhook_endpoints/ENDPOINT_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Update a webhook endpoint `PATCH /v1/webhook_endpoints/{endpoint_id}` Changes the URL, events, Projects or description, or turns the endpoint off and on. Turning it on clears a failing streak. Permission: `webhooks:write` #### Path parameters | Name | Description | | --- | --- | | `endpoint_id` | The endpoint's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | No | (up to 2048 characters) | | `description` | string or null | No | | | `enabled_events` | array of `*`, `post.created`, `post.published`, `post.partially_published`, `post.failed`, `post.deleted` | No | Event types to receive, or ["*"] for all. | | `project_ids` | array of string | No | | | `include_comment_content` | boolean | No | | | `enabled` | boolean | No | | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"webhook_endpoint"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `url` | string | Yes | | | `description` | string or null | Yes | | | `enabled_events` | array of string | Yes | | | `project_ids` | array of string | Yes | | | `api_version` | string | Yes | | | `include_comment_content` | boolean | Yes | | | `status` | `enabled`, `disabled` | Yes | | | `disabled_reason` | string or null | Yes | manual, gone (the endpoint answered 410) or failing (five days of failures). | | `failing_since` | string or null | Yes | | | `secret_last4` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl -X PATCH https://api.nowscale.com/v1/webhook_endpoints/ENDPOINT_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' ``` ### Delete a webhook endpoint `DELETE /v1/webhook_endpoints/{endpoint_id}` Deletes the endpoint and the deliveries still waiting for it. Permission: `webhooks:write` #### Path parameters | Name | Description | | --- | --- | | `endpoint_id` | The endpoint's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"webhook_endpoint"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `deleted` | `true` | Yes | | ```bash curl -X DELETE https://api.nowscale.com/v1/webhook_endpoints/ENDPOINT_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Roll a webhook secret `POST /v1/webhook_endpoints/{endpoint_id}/roll_secret` Creates a new signing secret. The old one keeps signing alongside it for 24 hours (two signatures per delivery) unless expire_now is true. Permission: `webhooks:write` · Accepts `Idempotency-Key` #### Path parameters | Name | Description | | --- | --- | | `endpoint_id` | The endpoint's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `expire_now` | boolean | No | | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"webhook_endpoint"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `url` | string | Yes | | | `description` | string or null | Yes | | | `enabled_events` | array of string | Yes | | | `project_ids` | array of string | Yes | | | `api_version` | string | Yes | | | `include_comment_content` | boolean | Yes | | | `status` | `enabled`, `disabled` | Yes | | | `disabled_reason` | string or null | Yes | manual, gone (the endpoint answered 410) or failing (five days of failures). | | `failing_since` | string or null | Yes | | | `secret_last4` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `secret` | string | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/webhook_endpoints/ENDPOINT_ID/roll_secret \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Send a test event `POST /v1/webhook_endpoints/{endpoint_id}/test` Queues a webhook_endpoint.test event to this endpoint, whatever its event filter. Permission: `webhooks:write` · Answers `202` #### Path parameters | Name | Description | | --- | --- | | `endpoint_id` | The endpoint's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"webhook_delivery"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `event_id` | string | Yes | (up to 128 characters) | | `status` | `"pending"` | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/webhook_endpoints/ENDPOINT_ID/test \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' ``` ### List deliveries `GET /v1/webhook_endpoints/{endpoint_id}/deliveries` Lists an endpoint's deliveries of the last 30 days, newest first, with their attempts' outcome. Permission: `webhooks:read` #### Path parameters | Name | Description | | --- | --- | | `endpoint_id` | The endpoint's id. | #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | No | Items per page, 1 to 100. (1 to 100, default 25) | | `cursor` | string | No | The next_cursor of the previous page. (up to 512 characters) | | `status` | `pending`, `delivering`, `succeeded`, `failed` | No | | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"list"` | Yes | | | `data` | array of object | Yes | | | `data[].object` | `"webhook_delivery"` | Yes | | | `data[].id` | string | Yes | (up to 128 characters) | | `data[].endpoint_id` | string | Yes | (up to 128 characters) | | `data[].event_id` | string | Yes | (up to 128 characters) | | `data[].event_type` | string | Yes | | | `data[].status` | `pending`, `delivering`, `succeeded`, `failed` | Yes | | | `data[].attempts` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `data[].next_attempt_at` | string or null | Yes | | | `data[].last_status_code` | integer or null | Yes | | | `data[].last_error` | string or null | Yes | | | `data[].delivered_at` | string or null | Yes | | | `data[].created_at` | string | Yes | ISO 8601 date and time, UTC. | | `has_more` | boolean | Yes | | | `next_cursor` | string or null | Yes | | ```bash curl https://api.nowscale.com/v1/webhook_endpoints/ENDPOINT_ID/deliveries \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` ### Retry a delivery `POST /v1/webhook_deliveries/{delivery_id}/retry` Sends a delivery again now, keeping its webhook-id, whatever its status. Permission: `webhooks:write` · Answers `202` #### Path parameters | Name | Description | | --- | --- | | `delivery_id` | The delivery's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"webhook_delivery"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `endpoint_id` | string | Yes | (up to 128 characters) | | `event_id` | string | Yes | (up to 128 characters) | | `event_type` | string | Yes | | | `status` | `pending`, `delivering`, `succeeded`, `failed` | Yes | | | `attempts` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `next_attempt_at` | string or null | Yes | | | `last_status_code` | integer or null | Yes | | | `last_error` | string or null | Yes | | | `delivered_at` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl -X POST https://api.nowscale.com/v1/webhook_deliveries/DELIVERY_ID/retry \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' ``` Source: https://docs.nowscale.dev/reference/webhooks --- # Usage > 1 endpoint: retrieve api usage. - [Retrieve API usage](#retrieve-api-usage): `GET /v1/usage` ### Retrieve API usage `GET /v1/usage` Counts this Workspace's API requests per day over the last 30 days, with errors and rate-limited requests. Permission: `usage:read` #### Query parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `days` | integer | No | (1 to 30, default 30) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"usage"` | Yes | | | `from` | string | Yes | ISO 8601 date and time, UTC. | | `to` | string | Yes | ISO 8601 date and time, UTC. | | `totals` | object | Yes | | | `totals.requests` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `totals.errors` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `totals.rate_limited` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `days` | array of object | Yes | | | `days[].date` | string | Yes | | | `days[].requests` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `days[].errors` | integer | Yes | (-9007199254740991 to 9007199254740991) | | `days[].rate_limited` | integer | Yes | (-9007199254740991 to 9007199254740991) | ```bash curl https://api.nowscale.com/v1/usage \ -H "Authorization: Bearer $NOWSCALE_API_KEY" ``` Source: https://docs.nowscale.dev/reference/usage --- # AI > 1 endpoint: draft a reply to a comment. - [Draft a reply to a comment](#draft-a-reply-to-a-comment): `POST /v1/ai/comment_replies` ### Draft a reply to a comment `POST /v1/ai/comment_replies` Writes a short reply to one comment, in the comment's language, using the Workspace's Tokens. Send max_tokens: without it, or when the price is higher, the request is refused with the price and nothing is charged. Nothing is posted; reply with POST /v1/comments/threads/{thread_id}/replies when you are happy with it. In a sandbox the reply is simulated and free. Permission: `ai:use` · Counts 5 requests · Accepts `Idempotency-Key` · Answers `201` #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `comment` | string | Yes | The comment to answer. Treated as data, never as instructions. (up to 600 characters) | | `video_title` | string | No | Title of the video or post the comment is on, for context. (up to 200 characters) | | `language` | string | No | BCP 47 tag for the reply when the comment's language is unclear. (up to 35 characters, default "en") | | `max_tokens` | number or string | No | The most this call may cost, in Tokens. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"comment_reply"` | Yes | | | `reply` | string | Yes | | | `question` | string or null | Yes | A question the draft needs answered before it can be specific, if any. | | `tokens` | string | Yes | Tokens charged, as a decimal string. | | `simulated` | boolean | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/ai/comment_replies \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` Source: https://docs.nowscale.dev/reference/ai --- # Test helpers > 3 endpoints: add a test account, change a test account's behaviour, advance a post. - [Add a test account](#add-a-test-account): `POST /v1/test_helpers/accounts` - [Change a test account's behaviour](#change-a-test-account-s-behaviour): `POST /v1/test_helpers/accounts/{account_id}` - [Advance a post](#advance-a-post): `POST /v1/test_helpers/posts/{post_id}/advance` ### Add a test account `POST /v1/test_helpers/accounts` Adds a simulated account of a network to a Project in this sandbox, at once. It never reaches the network: posts to it publish through the simulator, following its preset and your caption's magic values. Plan limits per network apply as for real accounts. Test keys only. Permission: `accounts:write` · Accepts `Idempotency-Key` · Answers `201` #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `project_id` | string | Yes | (up to 128 characters) | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `preset` | `healthy`, `reauth_required`, `expiring`, `slow`, `flaky` | No | How the simulated network behaves: healthy, reauth_required, expiring, slow (publishes 90 seconds late) or flaky (about 3 in 10 targets fail). (default "healthy") | | `name` | string | No | (up to 120 characters) | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"account"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `name` | string | Yes | | | `handle` | string or null | Yes | | | `avatar_url` | string or null | Yes | | | `followers` | integer or null | Yes | | | `status` | `active`, `reconnect_required`, `error` | Yes | | | `connected_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl -X POST https://api.nowscale.com/v1/test_helpers/accounts \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` ### Change a test account's behaviour `POST /v1/test_helpers/accounts/{account_id}` Switches a test account's preset, for example to reauth_required to see how your integration handles an account that must reconnect, then back to healthy. Test keys only. Permission: `accounts:write` #### Path parameters | Name | Description | | --- | --- | | `account_id` | The account's id. | #### Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `preset` | `healthy`, `reauth_required`, `expiring`, `slow`, `flaky` | Yes | | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"account"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `name` | string | Yes | | | `handle` | string or null | Yes | | | `avatar_url` | string or null | Yes | | | `followers` | integer or null | Yes | | | `status` | `active`, `reconnect_required`, `error` | Yes | | | `connected_at` | string | Yes | ISO 8601 date and time, UTC. | ```bash curl -X POST https://api.nowscale.com/v1/test_helpers/accounts/ACCOUNT_ID \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ ... }' ``` ### Advance a post `POST /v1/test_helpers/posts/{post_id}/advance` Runs the simulator for this post now: every target whose time has come, including ones held by a delay, publishes or fails right away instead of on the next worker run. A target scheduled in the future stays scheduled; publish the post to send it now. Answers with the post after the simulator ran. Test keys only. Permission: `posts:publish` · Accepts `Idempotency-Key` #### Path parameters | Name | Description | | --- | --- | | `post_id` | The post's id. | #### Response | Field | Type | Required | Description | | --- | --- | --- | --- | | `object` | `"post"` | Yes | | | `id` | string | Yes | (up to 128 characters) | | `project_id` | string or null | Yes | | | `status` | `draft`, `scheduled`, `publishing`, `published`, `partial`, `failed`, `deleting` | Yes | | | `review_status` | `not_required`, `draft`, `in_review`, `approved`, `rejected` | Yes | | | `title` | string or null | Yes | | | `caption` | string or null | Yes | | | `visibility` | string | Yes | | | `scheduled_at` | string or null | Yes | | | `published_at` | string or null | Yes | | | `created_at` | string | Yes | ISO 8601 date and time, UTC. | | `updated_at` | string | Yes | ISO 8601 date and time, UTC. | | `targets` | array of object | Yes | | | `targets[].object` | `"post_target"` | Yes | | | `targets[].id` | string | Yes | (up to 128 characters) | | `targets[].account_id` | string | Yes | (up to 128 characters) | | `targets[].network` | `youtube`, `tiktok`, `instagram`, `facebook`, `threads`, `bluesky`, `mastodon`, `pinterest`, `telegram`, `twitch`, `x`, `discord` | Yes | | | `targets[].status` | `pending`, `uploading`, `uploaded`, `published`, `failed` | Yes | | | `targets[].stage` | string | Yes | | | `targets[].external_id` | string or null | Yes | | | `targets[].error_code` | string or null | Yes | | | `targets[].scheduled_at` | string or null | Yes | | ```bash curl -X POST https://api.nowscale.com/v1/test_helpers/posts/POST_ID/advance \ -H "Authorization: Bearer $NOWSCALE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ ... }' ``` Source: https://docs.nowscale.dev/reference/test-helpers