This reference is generated from api/openapi.json (OpenAPI 3.1.0, API version 1.0.0). Read the foundation guide for session handling, permissions, and examples.
Download the OpenAPI specification. The running service also publishes it at /api/openapi.json. API paths below are relative to your SongCollect service origin.
Versioned, language-independent SongCollect interface. Browser sessions use an HttpOnly cookie. Every mutation requires the configured APP_ORIGIN header; native clients must retain the cookie and send that origin too. Native token authentication and cross-origin browser hosting are future work. Repeated Idempotency-Key requests replay the committed response; PUT requires the quoted revision ETag in If-Match. Public routes expose active song heads only.
Authenticationπ
- developmentSession: apiKey, sent in cookie as
songcollect. - session: apiKey, sent in cookie as
__Host-songcollect.
Security alternatives listed on an endpoint mean either cookie is accepted. Endpoints marked public do not require a session.
Endpointsπ
GET /api/health/liveπ
Operation: live.
Authentication: public.
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: Health | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
GET /api/health/readyπ
Operation: ready.
Authentication: public.
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: Health | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody | |
| 503 | application/json: Health |
GET /api/v1/churchesπ
Operation: churches.
Authentication: session or developmentSession.
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: Churches | ||
| 401 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
GET /api/v1/churches/{churchId}/songsπ
Operation: list.
Authentication: session or developmentSession.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
churchId | path | Yes | string (uuid) | |
q | query | No | string | maxLength: 200 |
offset | query | No | integer (int64) | minimum: 0; maximum: 100000 |
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: Catalogue | ||
| 400 | application/json: ErrorBody | ||
| 401 | application/json: ErrorBody | ||
| 404 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
POST /api/v1/churches/{churchId}/songsπ
Operation: create.
Authentication: session or developmentSession.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
churchId | path | Yes | string (uuid) | |
Origin | header | Yes | string | |
Idempotency-Key | header | Yes | string (uuid) |
Request body (required)
application/json: MetadataInput
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 201 | application/json: SongResponse | ETag: string; Location: string | |
| 400 | application/json: ErrorBody | ||
| 401 | application/json: ErrorBody | ||
| 403 | application/json: ErrorBody | ||
| 404 | application/json: ErrorBody | ||
| 409 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
GET /api/v1/churches/{churchId}/songs/{songId}π
Operation: get.
Authentication: session or developmentSession.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
churchId | path | Yes | string (uuid) | |
songId | path | Yes | string (uuid) |
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: SongResponse | ETag: string β Quoted current revision | |
| 400 | application/json: ErrorBody | ||
| 401 | application/json: ErrorBody | ||
| 404 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
PUT /api/v1/churches/{churchId}/songs/{songId}π
Operation: update.
Authentication: session or developmentSession.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
churchId | path | Yes | string (uuid) | |
songId | path | Yes | string (uuid) | |
Origin | header | Yes | string | |
Idempotency-Key | header | Yes | string (uuid) | |
If-Match | header | Yes | string | Strong ETag from the base revision, e.g. "1" |
Request body (required)
application/json: MetadataInput
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: SongResponse | ETag: string | |
| 400 | application/json: ErrorBody | ||
| 401 | application/json: ErrorBody | ||
| 403 | application/json: ErrorBody | ||
| 404 | application/json: ErrorBody | ||
| 409 | application/json: ErrorBody | ||
| 412 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 428 | application/json: ErrorBody | ||
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
GET /api/v1/churches/{churchId}/songs/{songId}/revisionsπ
Operation: revisions.
Authentication: session or developmentSession.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
churchId | path | Yes | string (uuid) | |
songId | path | Yes | string (uuid) |
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: Revisions | ||
| 400 | application/json: ErrorBody | ||
| 401 | application/json: ErrorBody | ||
| 404 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
GET /api/v1/public/churches/{churchId}/songsπ
Operation: public_list.
Authentication: public.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
churchId | path | Yes | string (uuid) | |
q | query | No | string | maxLength: 200 |
offset | query | No | integer (int64) | minimum: 0; maximum: 100000 |
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: Catalogue | ||
| 400 | application/json: ErrorBody | ||
| 404 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
GET /api/v1/public/churches/{churchId}/songs/{songId}π
Operation: public_get.
Authentication: public.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
churchId | path | Yes | string (uuid) | |
songId | path | Yes | string (uuid) |
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 200 | application/json: SongResponse | ETag: string | |
| 400 | application/json: ErrorBody | ||
| 404 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
POST /api/v1/sessionπ
Operation: login.
Authentication: public.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
Origin | header | Yes | string | Configured APP_ORIGIN |
Request body (required)
application/json: Credentials
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 204 | Signed in; HttpOnly session cookie set | No body | |
| 400 | application/json: ErrorBody | ||
| 401 | application/json: ErrorBody | ||
| 403 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | application/json: ErrorBody | ||
| 500 | Unexpected server error | application/json: ErrorBody |
DELETE /api/v1/sessionπ
Operation: logout.
Authentication: public.
Parameters
| Name | Location | Required | Type | Constraints / description |
|---|---|---|---|---|
Origin | header | Yes | string |
Responses
| Status | Description | Body | Headers |
|---|---|---|---|
| 204 | Session revoked and cookie cleared | No body | |
| 403 | application/json: ErrorBody | ||
| 413 | Request body exceeds 32 KiB | application/json: ErrorBody | |
| 429 | Rate limit exceeded | application/json: ErrorBody | |
| 500 | Unexpected server error | application/json: ErrorBody |
Schemasπ
Catalogueπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
church | Yes | Church | |
hasMore | Yes | boolean | |
songs | Yes | array of Song |
JSON schema
{
"type": "object",
"required": [
"church",
"songs",
"hasMore"
],
"properties": {
"church": {
"$ref": "#/components/schemas/Church"
},
"hasMore": {
"type": "boolean"
},
"songs": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Song"
}
}
}
}Churchπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
id | Yes | string (uuid) | |
name | Yes | string | |
role | No | Role or null | |
timeZone | Yes | string |
JSON schema
{
"type": "object",
"required": [
"id",
"name",
"timeZone"
],
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"name": {
"type": "string"
},
"role": {
"oneOf": [
{
"$ref": "#/components/schemas/Role"
},
{
"type": "null"
}
]
},
"timeZone": {
"type": "string"
}
}
}Churchesπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
churches | Yes | array of Church |
JSON schema
{
"type": "object",
"required": [
"churches"
],
"properties": {
"churches": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Church"
}
}
}
}Credentialsπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
email | Yes | string (email) | maxLength: 254 |
password | Yes | string | minLength: 1; maxLength: 256 |
Unknown properties are rejected.
JSON schema
{
"type": "object",
"required": [
"email",
"password"
],
"properties": {
"email": {
"type": "string",
"format": "email",
"maxLength": 254
},
"password": {
"type": "string",
"maxLength": 256,
"minLength": 1
}
},
"additionalProperties": false
}ErrorBodyπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
current | No | Song or null | |
error | Yes | string |
JSON schema
{
"type": "object",
"required": [
"error"
],
"properties": {
"current": {
"oneOf": [
{
"$ref": "#/components/schemas/Song"
},
{
"type": "null"
}
]
},
"error": {
"type": "string"
}
}
}Healthπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
status | Yes | string |
JSON schema
{
"type": "object",
"required": [
"status"
],
"properties": {
"status": {
"type": "string"
}
}
}Metadataπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
alternateTitles | Yes | array of string | maxItems: 20 |
authors | Yes | array of string | maxItems: 30 |
copyright | Yes | string | maxLength: 1000 |
songbooks | Yes | array of Songbook | maxItems: 30 |
themes | Yes | array of string | maxItems: 30 |
title | Yes | string | minLength: 1; maxLength: 200 |
Unknown properties are rejected.
JSON schema
{
"type": "object",
"required": [
"title",
"alternateTitles",
"authors",
"copyright",
"themes",
"songbooks"
],
"properties": {
"alternateTitles": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 20
},
"authors": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 30
},
"copyright": {
"type": "string",
"maxLength": 1000
},
"songbooks": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Songbook"
},
"maxItems": 30
},
"themes": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 30
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1
}
},
"additionalProperties": false
}MetadataInputπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
alternateTitles | No | array of string | maxItems: 20 |
authors | No | array of string | maxItems: 30 |
copyright | No | string | maxLength: 1000 |
songbooks | No | array of Songbook | maxItems: 30 |
themes | No | array of string | maxItems: 30 |
title | Yes | string | minLength: 1; maxLength: 200 |
Unknown properties are rejected.
JSON schema
{
"type": "object",
"required": [
"title"
],
"properties": {
"alternateTitles": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 20
},
"authors": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 30
},
"copyright": {
"type": "string",
"maxLength": 1000
},
"songbooks": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Songbook"
},
"maxItems": 30
},
"themes": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 30
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1
}
},
"additionalProperties": false
}Revisionπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
createdAt | Yes | string (date-time) | |
id | Yes | string (uuid) | |
metadata | Yes | Metadata | |
version | Yes | integer (int32) |
JSON schema
{
"type": "object",
"required": [
"id",
"version",
"metadata",
"createdAt"
],
"properties": {
"createdAt": {
"type": "string",
"format": "date-time"
},
"id": {
"type": "string",
"format": "uuid"
},
"metadata": {
"$ref": "#/components/schemas/Metadata"
},
"version": {
"type": "integer",
"format": "int32"
}
}
}Revisionsπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
revisions | Yes | array of Revision |
JSON schema
{
"type": "object",
"required": [
"revisions"
],
"properties": {
"revisions": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Revision"
}
}
}
}Roleπ
Type: string. allowed: "Administrator", "Editor", "Viewer"
JSON schema
{
"type": "string",
"enum": [
"Administrator",
"Editor",
"Viewer"
]
}Songπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
id | Yes | string (uuid) | |
metadata | Yes | Metadata | |
revisionId | Yes | string (uuid) | |
updatedAt | Yes | string (date-time) | |
version | Yes | integer (int32) |
JSON schema
{
"type": "object",
"required": [
"id",
"version",
"revisionId",
"metadata",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"metadata": {
"$ref": "#/components/schemas/Metadata"
},
"revisionId": {
"type": "string",
"format": "uuid"
},
"updatedAt": {
"type": "string",
"format": "date-time"
},
"version": {
"type": "integer",
"format": "int32"
}
}
}SongResponseπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
song | Yes | Song |
JSON schema
{
"type": "object",
"required": [
"song"
],
"properties": {
"song": {
"$ref": "#/components/schemas/Song"
}
}
}Songbookπ
| Property | Required | Type | Constraints / description |
|---|---|---|---|
book | Yes | string | minLength: 1; maxLength: 200 |
number | Yes | string | minLength: 1; maxLength: 30 |
Unknown properties are rejected.
JSON schema
{
"type": "object",
"required": [
"book",
"number"
],
"properties": {
"book": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"number": {
"type": "string",
"maxLength": 30,
"minLength": 1
}
},
"additionalProperties": false
}