The comments API exposes read/write access to market comments and propagates
X-Correlation-Id through every handler and any outbound HTTP call made within
the same request lifecycle.
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/comments |
None | Root list endpoint |
POST |
/api/comments |
None | Create a comment |
GET |
/api/markets/:id/comments |
None | List comments for a market (cursor-paginated) |
Returns a paginated list of comments.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit |
integer 1–100 | No | Page size (default: 20) |
cursor |
string | No | Opaque cursor returned by the previous page |
Request headers
| Header | Description |
|---|---|
X-Correlation-Id |
Optional. Alphanumeric + hyphens + underscores, max 128 chars. A UUID v4 is generated when absent. Unsafe characters are silently stripped. |
Response headers
| Header | Description |
|---|---|
X-Correlation-Id |
The resolved correlation ID (passed-through or freshly generated). |
200 response
{
"data": [],
"nextCursor": null,
"message": "Comments fetched securely"
}Creates a new comment. If outboundUrl is supplied the service dispatches a
POST to that URL containing the comment payload; X-Correlation-Id is
forwarded automatically via fetchWithCorrelationId.
Outbound call failures are logged as warnings — they do not affect the
201 response returned to the caller.
Request headers
| Header | Description |
|---|---|
X-Correlation-Id |
Optional. Propagated to the outbound webhook call when outboundUrl is set. |
Request body
{
"marketId": "market-abc",
"body": "Great prediction!",
"authorAddress": "GABC...",
"outboundUrl": "https://notifications.example.com/webhook"
}| Field | Type | Required | Description |
|---|---|---|---|
marketId |
string | Yes | Target market ID |
body |
string 1–2000 chars | Yes | Comment text |
authorAddress |
string | No | Stellar address of the author |
outboundUrl |
URL string | No | Outbound webhook URL |
Response headers
| Header | Description |
|---|---|
X-Correlation-Id |
Resolved correlation ID, echoed back. |
201 response
{
"data": {
"id": "c-1753712345678",
"marketId": "market-abc",
"body": "Great prediction!",
"authorAddress": "GABC...",
"createdAt": "2026-07-28T16:00:00.000Z"
},
"message": "Comment created successfully"
}Returns cursor-paginated comments for a single market, ordered newest-first.
Path parameters
| Parameter | Description |
|---|---|
id |
Market ID |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit |
integer 1–100 | No | Page size (default: 20) |
cursor |
string | No | Opaque cursor from the previous page |
Request headers
| Header | Description |
|---|---|
X-Correlation-Id |
Optional. See rules above. |
Response headers
| Header | Description |
|---|---|
X-Correlation-Id |
Resolved correlation ID. |
200 response
{
"data": [
{
"id": "c-abc",
"marketId": "m-123",
"authorId": null,
"authorAddress": null,
"body": "I think YES wins.",
"moderationFlagged": false,
"moderationReason": null,
"createdAt": "2026-07-28T16:00:00.000Z"
}
],
"nextCursor": "eyJzb3J0VmFsdWUiOi..."
}All three endpoints share the same correlation ID resolution logic (implemented
in src/middleware/correlation.ts):
- Accept — the value from the incoming
X-Correlation-Idrequest header (priority 1), thenX-Request-Id(priority 2), then the pino-httpreq.id(priority 3). - Sanitise — strip characters outside
[A-Za-z0-9\-_]and truncate to 128 characters to prevent log-injection attacks. - Generate — if the sanitised value is empty, generate a fresh RFC 4122 UUID v4.
- Store — write the resolved ID into the
AsyncLocalStorage context so downstream
service code can call
getCorrelationId()without prop-drilling. - Echo — set
X-Correlation-Idon the HTTP response so callers can correlate their own traces. - Propagate — outbound HTTP calls made via
fetchWithCorrelationId(url, init)(exported fromsrc/middleware/correlation.ts) automatically inject the correlation ID from the current ALS context into the outgoing request'sX-Correlation-Idheader.
Every handler logs correlationId and reqId together so a single grep or
log query can reconstruct the full request chain:
{
"level": 30,
"correlationId": "client-corr-id-12345",
"reqId": "client-corr-id-12345",
"marketId": "m-123",
"msg": "market comments listed"
}Validation failures return a standard error envelope:
{
"error": {
"code": "validation_error",
"details": [...]
}
}HTTP status codes: 400 (validation), 404 (not found), 500 (unexpected).
All comments routes inherit the global anonymous rate limit
(rateLimitAnon) and the markets CORS allowlist (marketsCors()).