| Metadata | Value |
|---|---|
| Status | Active |
| Version | 1.1.0 |
| Last Updated | 2026-02-08 |
| Author | Sangeetha Grantha Team |
The Sangita Grantha API exposes REST endpoints under /v1 for
public, read-only access to the Carnatic Krithi catalog, and /v1/admin
for authenticated editorial and curation workflows.
This document captures the canonical contract:
- Authentication and transport assumptions.
- Read models (search and detail views).
- Admin mutations (Krithi/variant/tag/import workflows).
- Error model, pagination, and filtering.
Screen-level usage for admin and mobile apps is documented in
integration-spec.md and ui-to-api-mapping.md.
- Development:
http://localhost:8080 - Production:
https://api.sangitagrantha.org(placeholder)
- Public read endpoints do not require authentication.
- These include:
GET /v1/krithis/searchGET /v1/krithis/{id}- Reference lists (if exposed publicly) such as
GET /v1/composersGET /v1/ragasGET /v1/talasGET /v1/deitiesGET /v1/temples
- Admins authenticate via username/password (or external IdP in future):
POST /v1/admin/login
- Returns a JWT access token and optional refresh token with role claims.
- Admin requests include header:
Authorization: Bearer <accessToken>- Tokens include claims:
userId: UUIDemail: stringroles[]: array of role codes (e.g.admin,editor,reviewer).
- All production endpoints require HTTPS.
- JWT access tokens expire after a configured time (e.g. 1 hour); refresh strategy can be added later.
- Content-Type:
application/jsonfor request/response bodies. - Accept:
application/jsonfor responses.
Search and browse Krithis.
Request Query Parameters:
q: optional free-text query (title, incipit, lyrics substring)composerId: optional UUIDragaId: optional UUIDtalaId: optional UUIDdeityId: optional UUIDtempleId: optional UUIDtag: optional tag slug (e.g.navaratri,bhakti)language: optional language code (e.g.sa,ta,te,kn,ml,hi,en)page: page number (default: 1, min: 1)size: page size (default: 25, max: 100)
Behaviour:
- Only returns Krithis with
workflow_state = 'published'. - If
qis provided:- Searches
title_normalized,incipit_normalized, and lyrics substring via trigram index onkrithi_lyric_variants.lyrics.
- Searches
- Filtering is applied conjunctively (all provided filters must match).
Response (200 OK):
{
"items": [
{
"id": "uuid",
"title": "Vatapi Ganapatim",
"incipit": "Vātāpi gaṇapatim bhajeham",
"titleNormalized": "vatapi ganapatim",
"incipitNormalized": "vatapi ganapatim bhajeham",
"composerId": "uuid",
"primaryRagaId": "uuid",
"talaId": "uuid",
"deityId": "uuid",
"templeId": "uuid",
"primaryLanguage": "sa",
"musicalForm": "KRITHI",
"isRagamalika": false,
"workflowState": "PUBLISHED",
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-02T00:00:00Z"
}
],
"page": 1,
"size": 25,
"total": 123
}(Response body aligns with KrithiDto and KrithiSearchResult.)
Fetch full details for a single Krithi.
Path params:
id: UUID of Krithi.
Response (200 OK):
{
"krithi": { /* KrithiDto */ },
"composer": { /* ComposerDto */ },
"primaryRaga": { /* RagaDto or null */ },
"tala": { /* TalaDto or null */ },
"deity": { /* DeityDto or null */ },
"temple": { /* TempleDto or null */ },
"ragas": [ /* KrithiRagaDto[] with RagaDto embedded or referenced */ ],
"lyricVariants": [ /* KrithiLyricVariantDto[] */ ],
"sections": [ /* KrithiSectionDto[] + optional KrithiLyricSectionDto by variant */ ],
"notationVariants": [ /* KrithiNotationVariantDto[] - only for VARNAM/SWARAJATHI */ ],
"tags": [ /* TagDto[] */ ]
}Error Cases:
404 Not Foundwithcode = "not_found"ifiddoes not exist or is notpublished.
Depending on product decisions, some reference endpoints may be public:
GET /v1/composersGET /v1/ragasGET /v1/talasGET /v1/deitiesGET /v1/templesGET /v1/tags
Each returns a paginated list of the corresponding *Dto objects.
All endpoints under /v1/admin/** require admin JWT and role checks.
Admin-facing list of Krithis with extended filters.
Query parameters:
- Same as public
/v1/krithis/searchplus:workflowState: filter bydraft,in_review,published,archived.hasImports: optional boolean (Krithis linked toimported_krithis).
Response: same pagination shape as public search, but can include non-published Krithis.
Same data as public detail endpoint, but includes non-published Krithis and editorial metadata.
List imported Krithis for review.
Query parameters:
status:pending | in_review | mapped | rejected | discarded(string)sourceId: optional import source UUIDq: optional free-text (raw title/composer/lyrics)page,size: pagination
Response:
{
"items": [
{
"id": "uuid",
"importSourceId": "uuid",
"sourceKey": "https://karnatik.com/song.php?id=123",
"rawTitle": "Vatapi Ganapatim",
"rawComposer": "Dikshitar",
"rawRaga": "Hamsadhvani",
"rawTala": "Adi",
"rawLanguage": "Sanskrit",
"importStatus": "PENDING",
"mappedKrithiId": null,
"reviewerUserId": null,
"reviewerNotes": null,
"reviewedAt": null,
"createdAt": "2025-01-01T00:00:00Z"
}
],
"page": 1,
"size": 25,
"total": 42
}Returns full ImportedKrithiDto plus any parsed payload and
links to candidate canonical entities.
These are editorial mutations; there are no participant/user-facing mutations in v1 of Sangita Grantha.
- Request:
{
"email": "editor@example.org",
"password": "string"
}- Response (
200 OK):
{
"accessToken": "jwt",
"expiresInSeconds": 3600,
"user": {
"id": "uuid",
"email": "editor@example.org",
"fullName": "Editor Name",
"roles": ["editor"]
}
}Create a new Krithi.
- Request body aligns with
KrithiDtominus generated fields:
{
"title": "string",
"incipit": "string?",
"composerId": "uuid",
"primaryRagaId": "uuid?",
"talaId": "uuid?",
"deityId": "uuid?",
"templeId": "uuid?",
"primaryLanguage": "SA",
"musicalForm": "KRITHI",
"isRagamalika": false,
"sahityaSummary": "string?",
"notes": "string?"
}-
Response (
201 Created):KrithiDto. -
Rules:
- Default
workflowState = DRAFT. - All creates must write an
audit_logentry.
- Default
Update an existing Krithi (idempotent, full update or patch semantics, to be defined in implementation).
Create a new lyric variant for a Krithi.
- Request: subset of
KrithiLyricVariantDtowithout IDs or audit fields. - Response: created
KrithiLyricVariantDto.
Update lyric variant (language/script cannot be changed after creation in v1; text, sampradaya, labels, and primary flag can be changed).
Define structural sections (pallavi, anupallavi, charanams, etc.) for a Krithi.
Attach section text for a given lyric variant using
krithi_lyric_sections.
All these endpoints follow the patterns:
- Role checks (
editor/reviewer/admin). - Validation.
- Transaction via
DatabaseFactory.dbQuery {}. - Audit logging.
Notation endpoints are only applicable for compositions with musicalForm of VARNAM or SWARAJATHI.
Retrieve all notation variants for a Krithi.
- Response: Array of
KrithiNotationVariantDtoobjects, each containing:id,krithiId,notationType(SWARA | JATHI)talaId,kalai,eduppuOffsetBeatsvariantLabel,sourceReference,isPrimarynotationRows: Array ofKrithiNotationRowDtoobjects
Create a new notation variant.
- Request:
{
"notationType": "SWARA",
"talaId": "uuid?",
"kalai": 1,
"eduppuOffsetBeats": 0,
"variantLabel": "string?",
"sourceReference": "string?",
"isPrimary": false,
"notationRows": [
{
"sectionId": "uuid",
"orderIndex": 0,
"swaraText": "S R G M P D N S",
"sahityaText": "string?",
"talaMarkers": "string?"
}
]
}- Response: Created
KrithiNotationVariantDtowith all rows.
Update a notation variant (metadata only; rows updated separately).
Add or update notation rows for a variant.
- Request: Array of
KrithiNotationRowDtoobjects.
Delete a notation variant and all its rows.
Create new tag.
Update tag metadata.
Assign tags to a Krithi.
- Request:
{
"tagIds": ["uuid", "uuid"]
}Unassign a single tag from a Krithi.
Map an ImportedKrithi to a canonical Krithi.
- Request:
{
"krithiId": "uuid",
"notes": "Mapped to existing Vatapi Ganapatim record"
}- Behaviour:
- Sets
mapped_krithi_id. - Moves
import_statustoMAPPED. - Writes audit log.
- Sets
Reject an imported entry.
- Sets
import_status = REJECTEDwith reviewer notes.
Scrape metadata and content from a supported external URL (e.g., shivkumar.org).
- Request:
ScrapeRequest
{ "url": "http://shivkumar.org/musical/..." }- Response:
ImportedKrithiDto(Created statusPENDING).
List imported records with optional filtering.
- Query Params:
status:PENDING,IMPORTED,REJECTED
AI-powered transliteration of lyrics or notation.
- Request:
TransliterationRequest
{
"content": "raw text...",
"targetScript": "latn",
"sourceScript": "deva"
}- Response:
TransliterationResponse
{
"transliterated": "transliterated text...",
"targetScript": "latn"
}{
"code": "string",
"message": "human readable summary",
"fields": {
"fieldName": "issue description"
},
"timestamp": "2025-12-21T10:30:00Z",
"requestId": "optional-request-id"
}| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | validation_error |
Validation errors on input |
| 401 | unauthorized |
Missing/invalid admin token |
| 403 | forbidden |
Lacking required role |
| 404 | not_found |
Resource not found |
| 409 | conflict |
Uniqueness or state conflict |
| 429 | throttled |
Rate limit exceeded (if applicable) |
| 500 | internal_error |
Unexpected server error |
Standard pagination for list endpoints:
{
"items": [...],
"page": 1,
"size": 25,
"total": 123
}Query parameters:
page: default 1size: default 25, max 100 for public, higher for admin where safe.
Typical filters:
- Krithis:
q,composerId,ragaId,talaId,deityId,templeId,tag,language,workflowState(admin only). - Imports:
status,sourceId,q.
- Public read endpoints: no authentication, only published Krithis.
- Admin endpoints:
POST /v1/admin/loginto obtain JWT.- JWT required for all
/v1/admin/**requests. - Role-based checks enforced at route or service boundaries.
- Any change to this contract must be reflected in:
- KMM shared DTOs (
modules/shared/domain). - Backend route handlers and services.
- Admin web and mobile integration specs.
- KMM shared DTOs (
- Backward incompatible changes require versioning (
/v2/...).
This contract describes intended v1 endpoints. Actual implementation status for Sangita Grantha will be tracked via:
- Backend route and service tests.
- Admin web UI integration.
- Migration notes in
database/migrations/.
Unset endpoints MUST return 501 Not Implemented with
code = "not_implemented" until they are complete.