---
title: Publish a batch of releases
description: >-
  Inserts or updates releases for one source, identified by slug or src_… ID on
  the bare path, or by the org-scoped slug pair.
---
# Publish a batch of releases

## POST /sources/{slug}/releases/batch

`POST https://api.releases.sh/v1/sources/{slug}/releases/batch`

Inserts or updates releases for one source, identified by slug or `src_…` ID on the bare path, or by the org-scoped slug pair. Releases are matched on `url`: a new URL inserts, a known URL fills empty fields, or, with `mode: "upsert-content"`, overwrites content and media. Re-posting the same batch is safe. Requires a `write` token, or a publish token bound to this source. A source's first write with its owner's publish token marks it push-fed, so it is no longer polled.

**Auth:** Bearer token required.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `slug` | path | string | yes |  |

**Request body** (`application/json`)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `mode` | string | no |  |
| `releases` | object[] | yes |  |

**Responses**

- `200` Batch result: inserted count, the source's new release total, and the new IDs
- `400` `releases` is not an array, or `mode` is not `upsert-content`
- `401` Bearer token required
- `403` Token lacks write scope, or the publish token is bound to another source
- `404` Source not found
- `500` D1 insert failed

## POST /orgs/{orgSlug}/sources/{sourceSlug}/releases/batch

`POST https://api.releases.sh/v1/orgs/{orgSlug}/sources/{sourceSlug}/releases/batch`

Inserts or updates releases for one source, identified by slug or `src_…` ID on the bare path, or by the org-scoped slug pair. Releases are matched on `url`: a new URL inserts, a known URL fills empty fields, or, with `mode: "upsert-content"`, overwrites content and media. Re-posting the same batch is safe. Requires a `write` token, or a publish token bound to this source. A source's first write with its owner's publish token marks it push-fed, so it is no longer polled.

**Auth:** Bearer token required.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgSlug` | path | string | yes |  |
| `sourceSlug` | path | string | yes |  |

**Request body** (`application/json`)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `mode` | string | no |  |
| `releases` | object[] | yes |  |

**Responses**

- `200` Batch result: inserted count, the source's new release total, and the new IDs
- `400` `releases` is not an array, or `mode` is not `upsert-content`
- `401` Bearer token required
- `403` Token lacks write scope, or the publish token is bound to another source
- `404` Source not found
- `500` D1 insert failed

Full request and response schemas: [OpenAPI 3.1 spec](https://api.releases.sh/v1/openapi.json).
