---
title: POST /api/articles/[id]/publish
description: Publish an article — assign a slug once, mark it published, stamp publishedAt, and return the public /story/<slug> URL. Idempotent.
---

<Note>

The CLI equivalent is `scripto articles publish <id>`, which prints the URL to paste into Medium's "Import a story".

</Note>

Publishes the article (owner-scoped): assigns a slug **once**, marks it `published`, stamps `publishedAt` the first time, and returns the absolute public URL Medium's importer fetches. **Idempotent** — re-publishing keeps the existing slug and `publishedAt`.

## Endpoint

```text
POST https://scripto.codika.io/api/articles/{id}/publish
```

## Auth

`Authorization: Bearer scripto_…`

## Request

No body.

## Response (200)

```json
{
  "success": true,
  "data": {
    "slug": "our-launch",
    "url": "https://scripto.codika.io/story/our-launch",
    "status": "published"
  }
}
```

The slug is minted from the title; on a collision a short, stable suffix (the last 6 of the uuid) is appended, so the same article always resolves to the same slug.

## Errors

| HTTP | `code` | Cause |
|---|---|---|
| 401 | `unauthenticated` | Missing or invalid key. |
| 404 | `not-found` | No such article, or you don't own it. |
| 500 | `internal` | Backend error. |

## curl

```bash
curl -sS -X POST "https://scripto.codika.io/api/articles/$ID/publish" \
  -H "Authorization: Bearer $SCRIPTO_API_KEY"
```

## Next

- **[Import, don't paste](/concepts/import-model)** — what happens when Medium fetches the URL.
- **[POST /api/articles/import](/api-reference/import-article)** — the reverse direction.
