---
url: /quickstart.md
---

# Quickstart

You can publish videos using either the **socialized.dev CLI** (recommended) or direct **REST API requests**.

## CLI Quickstart (Recommended)

The `@socialized.dev/cli` wraps the multipart upload ceremony and publication dispatch into simple commands.

### 1. Install and Authenticate

Install the CLI globally and authenticate with your API key from [dash.socialized.dev](https://dash.socialized.dev):

```sh
npm install -g @socialized.dev/cli

# Log in using your API key
socialized.dev login --key sk_your_api_key
```

### 2. Select a Brand

If you have multiple brands, list and set the active brand:

```sh
# List available brands
socialized.dev brands list

# Select a brand to use as the default
socialized.dev brands use brand_...
```

### 3. Publish

Publish a local video file to one or more platforms (e.g. YouTube and Instagram). The CLI automatically handles the multipart asset upload internally, dispatches the publication, and optionally watches the status until it resolves:

```sh
socialized.dev publications create \
  --video ./my-clip.mp4 \
  --cover ./my-cover.jpg \
  --to youtube,instagram \
  --title "My First Dispatch" \
  --description "Posted once, everywhere." \
  --watch
```

***

## REST API Quickstart (curl)

Publish a video to a connected destination, end to end. Everything here is plain
HTTP against `https://api.socialized.dev`. Set your credentials and retrieve your
brand ID:

```sh
export SK="sk_your_api_key"          # from dash.socialized.dev → Developer
export API="https://api.socialized.dev/api/v1"

# List your brands and pick one (the id looks like brand_…)
curl -s "$API/brands" -H "Authorization: Bearer $SK"
export BRAND="brand_…"
```

Every call authenticates with `Authorization: Bearer $SK`. Brand-scoped resources
(like publications) are nested under `/brands/$BRAND`, while account-scoped resources
(like assets) are top-level under `/assets`.

## 1. Connect an account (one time, in the dashboard)

Connecting an account uses OAuth, so it happens in a browser, not over curl:
open [dash.socialized.dev](https://dash.socialized.dev), and connect a YouTube,
Instagram, or Facebook account. You no longer need a connection ID to publish: you
publish directly by platform name.

## 2. Start an upload

Tell the API about the file; it returns a presigned plan: one `PUT` URL per
part (one part for a small clip) plus the `assetId` of the video asset.

```sh
curl -s -X POST "$API/assets" \
  -H "Authorization: Bearer $SK" -H "Content-Type: application/json" \
  -d '{ "filename": "clip.mp4", "contentType": "video/mp4", "sizeBytes": 5242880 }' | jq
```

```json
{
  "uploadId": "…",
  "assetId": "asset_video_…",
  "partSize": 10485760,
  "parts": [{ "partNumber": 1, "url": "https://…upload-url…/clip.mp4?partNumber=1&…" }]
}
```

To set a custom cover, upload an image as its own asset first. Send a `POST` to
`$API/assets` with an image `contentType` (e.g. `image/jpeg`). The API returns a presigned
`PUT` URL to upload the cover to. (You do not host it yourself.)

```sh
curl -s -X POST "$API/assets" \
  -H "Authorization: Bearer $SK" -H "Content-Type: application/json" \
  -d '{ "filename": "cover.jpg", "contentType": "image/jpeg", "sizeBytes": 204800 }' | jq
```

```json
{
  "uploadId": "…",
  "assetId": "asset_cover_…",
  "partSize": 10485760,
  "parts": [{ "partNumber": 1, "url": "https://…upload-url…/cover.jpg?partNumber=1&…" }]
}
```

Upload the cover bytes and complete the cover upload the same way as the video. This gives you a `coverAssetId` to use when publishing.

## 3. Upload the bytes

`PUT` the file to each part's presigned URL. Keep each response's `ETag` header.

The part URLs are signed for the `host` header only, so you don't need to replay any headers
from the plan: send a plain `PUT` with the bytes as the body and a normal `Content-Length`
(every HTTP client sets it for you — don't use chunked transfer encoding). A `Content-Type`
on a part is ignored; the stored asset's type is the `contentType` you gave `POST /assets`.

```sh
curl -s -X PUT --upload-file clip.mp4 "<parts[0].url>" -D - -o /dev/null | grep -i etag
# etag: "abc123…"
```

Or from Node (any `fetch` runtime works the same):

```js
const res = await fetch(part.url, { method: 'PUT', body: await fs.readFile('clip.mp4') });
const etag = res.headers.get('etag');
```

## 4. Complete the upload

Hand back the part numbers + ETags. The asset is recorded and becomes `ready`. Quoted
(`"abc123…"`, as the header arrives) and unquoted (`abc123…`) ETags are both accepted.

```sh
curl -s -X POST "$API/assets/<uploadId>/complete" \
  -H "Authorization: Bearer $SK" -H "Content-Type: application/json" \
  -d '{ "parts": [{ "partNumber": 1, "etag": "abc123…" }] }' | jq
# { "assetId": "asset_video_…", "status": "ready" }
```

## 5. Publish

One call fans the video asset out to every target. Send an
[`Idempotency-Key`](/errors#idempotency) so a retry is safe.

```sh
curl -s -X POST "$API/brands/$BRAND/publications" \
  -H "Authorization: Bearer $SK" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "videoAssetId": "asset_video_…",
    "coverAssetId": "asset_cover_…",
    "title": "My first dispatch",
    "description": "Posted once, everywhere.",
    "targets": [{ "platform": "youtube" }]
  }' | jq
```

```json
{
  "publicationId": "pub_…",
  "status": "queued",
  "targets": [{ "id": "tgt_…", "platform": "youtube", "status": "pending" }]
}
```

## 6. Watch it go live

Poll the publication (or subscribe to a [webhook](/webhooks) and skip polling).
Each target travels `pending → processing → published`; the publication
aggregates to `done`, `partial`, or `failed`.

```sh
curl -s "$API/brands/$BRAND/publications/pub_…" -H "Authorization: Bearer $SK" | jq '.publication.status, .publication.targets[] | {platform, status, externalUrl}'
```

When a target reaches `published`, its `externalUrl` is the live post.

***

That's the whole loop: **upload once, publish everywhere, watch it settle.** Next:
[Authentication](/authentication) for keys and brands, [Publishing a video](/publishing)
for the full model, or the [REST API reference](https://api.socialized.dev/reference) for every endpoint.
