Kreosus API

The Kreosus creator API gives your own code the data behind your creator panel: supporters, payments, subscriptions, statistics and payouts. It can also write, publish and delete posts. Requests and responses are JSON over HTTPS.

Base URL
https://kreosus.com/api/v1

Quick Start

  1. Open API Access in your creator panel and create a key. Tick the posts option if your code will write posts.
  2. Copy the key at once. We show it one time only and store only its hash.
  3. Send it in the X-API-Key header with every request.
curl "https://kreosus.com/api/v1/profile" \
  -H "X-API-Key: $KREOSUS_API_KEY"

Authentication

Send your key in the X-API-Key header. A profile has one key at a time. Creating a new key ends the old one at once, and revoking a key ends it too. A missing or wrong key gets 401. After 20 failed attempts in a minute from one IP address, further attempts get 429.

Every response carries an X-Request-Id header. Include this value when you contact us about a request.

Permissions

Every key can read. Writing posts needs the posts:write permission, which you choose when you create the key. A request outside the key's permissions gets 403 with the code INSUFFICIENT_SCOPE, and error.details.required_scope names the permission it needs.

readRead your profile, supporters, payments, subscriptions, statistics, payouts and posts.
posts:writeCreate, edit, publish and delete posts.

Errors

A failed request answers with an HTTP status and a JSON body. error.code is stable, so branch on it. error.message is for people and can change. A wrong parameter is named in error.field_errors.

{
  "status": false,
  "error": {
    "message": "…",
    "code": "INVALID_FORMAT",
    "field_errors": { "from": "…" }
  }
}
400VALIDATION_FAILED, INVALID_FORMATA parameter or a body field is not valid.
401AUTHENTICATION_REQUIREDThe key is missing or not valid.
403INSUFFICIENT_SCOPE, PERMISSION_DENIEDThe key lacks the permission, or the profile may not post.
404RESOURCE_NOT_FOUNDThe endpoint or the post does not exist, or the post is not yours.
405INVALID_REQUESTThe endpoint does not accept this method. The Allow header names the one it accepts.
409RESOURCE_CONFLICTThe post is already live.
422IDEMPOTENCY_KEY_REUSEDThe Idempotency-Key was used before with a different request.
429RATE_LIMIT_EXCEEDEDToo many requests. Wait for the seconds in Retry-After.

Rate Limits

Each endpoint has its own quota per profile, counted over one minute: 30 requests for reads, 15 for statistics and 10 for writes. Each endpoint below shows its quota.

Every response to a request with a valid key reports your remaining quota in the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. Reset is the number of seconds until the window renews. On a 429 response, the Retry-After header tells you how many seconds to wait.

Lists, Dates and Amounts

Lists take page (from 1) and limit (1 to 100, 20 by default) and answer with items, total, page, limit and has_more. A limit above 100 returns 100 rows. A page or limit that is not a positive whole number gets 400.

from and to take a date (2026-10-01), which means the whole day in Istanbul time, or an RFC 3339 timestamp (2026-10-01T09:30:00Z), which means that exact moment. To fetch only what changed since your last sync, send the time of that sync as from. Any other format gets 400.

Dates and times are returned in Istanbul time (UTC+3).

Every amount comes twice: as a decimal number, such as amount: 12.5, and as a whole number in the smallest unit of the currency (kuruş or cents), such as amount_minor: 1250. Use the fields that end in _minor when you add up or compare amounts, because decimal numbers can lose a fraction of a cent.

Conditional Requests

Every successful GET answer carries an ETag header. Send that value back in If-None-Match. If the data has not changed, the answer is 304 with no body, so a client that polls often moves less data. A 304 still counts against the quota. Webhooks tell you about payments and subscriptions without polling.

curl -i "https://kreosus.com/api/v1/subscriptions?status=1" \
  -H "X-API-Key: $KREOSUS_API_KEY" \
  -H 'If-None-Match: W/"5d41402abc4b2a76b9719d911017c592"'

HTTP/2 304

Retries Without Duplicates

Creating and publishing a post accept an Idempotency-Key header: any unique string up to 255 characters, such as a UUID. If the connection drops, send the same request with the same key. For 24 hours you get the first answer again, with an Idempotent-Replayed: true header, and no second post. The same key with a different body gets 422. Only a success is kept: after an error, a retry runs again. A retry sent while the first request still runs gets 409 with a Retry-After header.

Account Data

Profile Info

GET/api/v1/profile

Returns your profile information, support tiers, and active goals.

Permission read30 req/min

Response

  • creator.idinteger
  • creator.titlestring
  • creator.urlstring
  • creator.currencystring
  • creator.imagestring | null
  • tiers[].idinteger
  • tiers[].titlestring
  • tiers[].descriptionstring
  • tiers[].amountnumber
  • tiers[].amount_minorinteger
  • tiers[].currencystring
  • goals[].idinteger
  • goals[].targetinteger
  • goals[].descriptionstring
  • goals[].imagestring | null
curl "https://kreosus.com/api/v1/profile" \
  -H "X-API-Key: $KREOSUS_API_KEY"

List Supporters

GET/api/v1/supporters

Everyone who has paid this profile, the largest total first.

Permission read30 req/min

Parameters

NameTypeDefaultDescription
pageinteger1Page number
limitinteger20Rows per page. (max 100)
searchstring-Search by name or email
fromstring (date-time)-Start, a date or an RFC 3339 timestamp.
tostring (date-time)-End, a date or an RFC 3339 timestamp.

Response

  • items[].idstring
  • items[].namestring
  • items[].emailstring
  • items[].total_amountnumber
  • items[].total_amount_minorinteger
  • items[].currencystring
  • items[].is_subscriberboolean
  • items[].first_payment_datestring
  • items[].vat_countrystring | null
  • items[].vat_percentnumber
curl "https://kreosus.com/api/v1/supporters?limit=10" \
  -H "X-API-Key: $KREOSUS_API_KEY"

List Payments

GET/api/v1/payments

Every payment to this profile, the newest first, with its fees and the net amount.

Permission read30 req/min

Parameters

NameTypeDefaultDescription
pageinteger1Page number
limitinteger20Rows per page. (max 100)
statusinteger-1 succeeded, 2 failed, 3 refunded, 4 reversed as fraud. One of: 1, 2, 3, 4
fromstring (date-time)-Start, a date or an RFC 3339 timestamp.
tostring (date-time)-End, a date or an RFC 3339 timestamp.

Response

  • items[].idstring
  • items[].supporter_namestring
  • items[].supporter_emailstring
  • items[].amountnumber
  • items[].amount_minorinteger
  • items[].currencystring
  • items[].vat_amountnumber
  • items[].vat_amount_minorinteger
  • items[].stripe_feenumber
  • items[].stripe_fee_minorinteger
  • items[].platform_feenumber
  • items[].platform_fee_minorinteger
  • items[].net_amountnumber
  • items[].net_amount_minorinteger
  • items[].statusinteger
  • items[].messagestring | null
  • items[].is_subscriptionboolean
  • items[].datestring
curl "https://kreosus.com/api/v1/payments?from=2026-10-01T00:00:00%2B03:00&status=1" \
  -H "X-API-Key: $KREOSUS_API_KEY"

List Subscriptions

GET/api/v1/subscriptions

Monthly subscriptions to this profile.

Permission read30 req/min

Parameters

NameTypeDefaultDescription
pageinteger1Page number
limitinteger20Rows per page. (max 100)
statusinteger-1 active, 0 canceled. One of: 0, 1

Response

  • items[].idstring
  • items[].supporter_namestring
  • items[].supporter_emailstring
  • items[].amountnumber
  • items[].amount_minorinteger
  • items[].currencystring
  • items[].statusinteger
  • items[].subscription_datestring
  • items[].last_payment_datestring | null
  • items[].vat_countrystring | null
  • items[].vat_percentnumber
  • items[].total_paidnumber
  • items[].total_paid_minorinteger
curl "https://kreosus.com/api/v1/subscriptions?status=1" \
  -H "X-API-Key: $KREOSUS_API_KEY"

Statistics

GET/api/v1/statistics

Totals for a range of whole days, next to the all-time totals and the current balance.

Permission read15 req/min

Parameters

NameTypeDefaultDescription
fromstring (date)Last 30 daysFirst day (YYYY-MM-DD).
tostring (date)TodayLast day (YYYY-MM-DD).

Response

  • currencystring
  • period.fromstring
  • period.tostring
  • total_amountnumber
  • total_amount_minorinteger
  • total_amount_all_timenumber
  • total_amount_all_time_minorinteger
  • total_transactionsinteger
  • total_transactions_all_timeinteger
  • new_subscribersinteger
  • new_subscribers_all_timeinteger
  • active_monthly_supportersinteger
  • one_time_supportersinteger
  • account_balancenumber
  • account_balance_minorinteger
  • stripe_feenumber
  • stripe_fee_minorinteger
  • platform_feenumber
  • platform_fee_minorinteger
curl "https://kreosus.com/api/v1/statistics?from=2026-01-01&to=2026-03-31" \
  -H "X-API-Key: $KREOSUS_API_KEY"

Payout Requests

GET/api/v1/payouts

Your payout requests, the newest first.

Permission read30 req/min

Parameters

NameTypeDefaultDescription
pageinteger1Page number
limitinteger20Rows per page. (max 100)

Response

  • items[].idstring
  • items[].amountnumber
  • items[].amount_minorinteger
  • items[].currencystring
  • items[].statusinteger
  • items[].payout_typestring | null
  • items[].create_datestring
  • items[].notestring | null
curl "https://kreosus.com/api/v1/payouts?limit=50" \
  -H "X-API-Key: $KREOSUS_API_KEY"

Posts

List Posts

GET/api/v1/posts

Your live posts, or your drafts, the newest first. You see every post whole, also the ones only supporters can open.

Permission read30 req/min

Parameters

NameTypeDefaultDescription
pageinteger1Page number
limitinteger20Rows per page. (max 100)
statusstringliveLive posts or drafts. One of: live, draft

Response

  • items[].idinteger
  • items[].urlstring
  • items[].statusstring
  • items[].contentstring
  • items[].imagesstring[]
  • items[].visibilitystring
  • items[].visibility_scheduleobject[] | null
  • items[].locked_viewstring
  • items[].like_countinteger
  • items[].comment_countinteger
  • items[].created_atstring
  • items[].edited_atstring | null
curl "https://kreosus.com/api/v1/posts?status=draft" \
  -H "X-API-Key: $KREOSUS_API_KEY"

Get a Post

GET/api/v1/posts/{id}

One post or draft of yours, by the id in its address.

Permission read30 req/min

Parameters

NameTypeDefaultDescription
idinteger-The post id, as in the post's address.

Response

  • post.idinteger
  • post.urlstring
  • post.statusstring
  • post.contentstring
  • post.imagesstring[]
  • post.visibilitystring
  • post.visibility_scheduleobject[] | null
  • post.locked_viewstring
  • post.like_countinteger
  • post.comment_countinteger
  • post.created_atstring
  • post.edited_atstring | null
curl "https://kreosus.com/api/v1/posts/1024" \
  -H "X-API-Key: $KREOSUS_API_KEY"

Create a Post

POST/api/v1/posts

Publishes a post at once, or saves it as a draft. A post needs text or at least one image. Mentioned users get a notification, as they do for a post from the panel. Answers 201 with the post.

Permission posts:write10 req/minAccepts Idempotency-Key

JSON Body

NameTypeDefaultDescription
contentstring-The text, up to 10,000 characters. Required unless you send images.
visibilitystringpublicWho can open the post. One of: public, active_supporters, all_supporters, custom
schedulearray-With visibility custom, up to 4 stages, each wider than the one before: {"audience": "tier", "tier_id": 12, "days": 3}. Audience is tier, active_supporters, all_supporters or public. Every stage but the last has days (1 to 365).
locked_viewstringteaser_countdownWith visibility custom, what others see before their stage opens: teaser_countdown, teaser or hidden.
imagesarray-Up to 4 images as base64 data URIs (data:image/jpeg;base64,...), each 100 to 4096 pixels wide and high. An image we cannot read is left out.
draftbooleanfalsetrue saves a draft instead of publishing.

Response

  • post.idinteger
  • post.urlstring
  • post.statusstring
  • post.contentstring
  • post.imagesstring[]
  • post.visibilitystring
  • post.visibility_scheduleobject[] | null
  • post.locked_viewstring
  • post.like_countinteger
  • post.comment_countinteger
  • post.created_atstring
  • post.edited_atstring | null
curl -X POST "https://kreosus.com/api/v1/posts" \
  -H "X-API-Key: $KREOSUS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"content": "Yeni bölüm yayında!", "visibility": "all_supporters"}'

Edit a Post

PATCH/api/v1/posts/{id}

Changes only the fields you send. A field you leave out keeps its value.

Permission posts:write10 req/min

Parameters

NameTypeDefaultDescription
idinteger-The post id, as in the post's address.

JSON Body

NameTypeDefaultDescription
contentstring-The text, up to 10,000 characters. Required unless you send images.
visibilitystring-Who can open the post. One of: public, active_supporters, all_supporters, custom
schedulearray-With visibility custom, up to 4 stages, each wider than the one before: {"audience": "tier", "tier_id": 12, "days": 3}. Audience is tier, active_supporters, all_supporters or public. Every stage but the last has days (1 to 365).
locked_viewstring-With visibility custom, what others see before their stage opens: teaser_countdown, teaser or hidden.
imagesarray-Up to 4 images as base64 data URIs (data:image/jpeg;base64,...), each 100 to 4096 pixels wide and high. An image we cannot read is left out.
keep_imagesarray-Positions (from 0) of the current images to keep. Leave out both keep_images and images to keep every image. New images in images come after the kept ones, 4 in total at most.

Response

  • post.idinteger
  • post.urlstring
  • post.statusstring
  • post.contentstring
  • post.imagesstring[]
  • post.visibilitystring
  • post.visibility_scheduleobject[] | null
  • post.locked_viewstring
  • post.like_countinteger
  • post.comment_countinteger
  • post.created_atstring
  • post.edited_atstring | null
curl -X PATCH "https://kreosus.com/api/v1/posts/1024" \
  -H "X-API-Key: $KREOSUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Yeni bölüm yayında! Bağlantı açıklamada."}'

Delete a Post

DELETE/api/v1/posts/{id}

Deletes a post or a draft. You cannot undo this.

Permission posts:write10 req/min

Parameters

NameTypeDefaultDescription
idinteger-The post id, as in the post's address.

Response

  • idinteger
  • deletedboolean
curl -X DELETE "https://kreosus.com/api/v1/posts/1024" \
  -H "X-API-Key: $KREOSUS_API_KEY"

Publish a Draft

POST/api/v1/posts/{id}/publish

Publishes a draft. A post that is already live gets 409.

Permission posts:write10 req/minAccepts Idempotency-Key

Parameters

NameTypeDefaultDescription
idinteger-The post id, as in the post's address.

Response

  • post.idinteger
  • post.urlstring
  • post.statusstring
  • post.contentstring
  • post.imagesstring[]
  • post.visibilitystring
  • post.visibility_scheduleobject[] | null
  • post.locked_viewstring
  • post.like_countinteger
  • post.comment_countinteger
  • post.created_atstring
  • post.edited_atstring | null
curl -X POST "https://kreosus.com/api/v1/posts/1024/publish" \
  -H "X-API-Key: $KREOSUS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Webhook Events

When a payment or subscription changes, Kreosus sends a signed JSON request to your HTTPS address, so you do not have to poll the API.

payment.succeeded payment.refunded payment.reversed subscription.created subscription.canceled ping

Events: payment.succeeded, payment.refunded, payment.reversed, subscription.created, subscription.canceled. payment.reversed means the payment was taken back as fraud or a lost chargeback. End access on it as on payment.refunded. Answer 2xx to event types you do not handle: we may add new ones, and an event that keeps failing turns the webhook off. The data.object field is the same record the matching API endpoint returns, as it was when the event happened.

Each request carries a Kreosus-Signature header of the form t=<timestamp>,v1=<signature>. The signature is the HMAC-SHA256 of "<timestamp>.<request body>" with your signing secret. Verify it and reject timestamps older than 5 minutes.

import crypto from 'node:crypto';

// rawBody: the request body exactly as received, before any JSON parsing.
export function verifyKreosusSignature(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((part) => part.split('=')));
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && v1?.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

If your receiver does not answer with a 2xx within 5 seconds, we retry after 1 min, 5 min, 30 min, 1 h, 3 h, 6 h, 12 h and 24 h. The same event can arrive more than once. Use the Kreosus-Event-Id header to drop duplicates. If an event still fails on the last retry and no other event got through in that time, we turn the webhook off and email you. While it is off, no new events are queued. It turns back on when a test event is delivered or you save the address again.

Changes

2026-10-23
Post endpoints, key permissions, ETag and If-None-Match, Idempotency-Key, RFC 3339 timestamps in from and to, 400 for wrong parameters, amounts in minor units (_minor fields), and the OpenAPI document.
2026-10-18
Signed webhooks for payments and subscriptions. Quota headers and X-Request-Id on every answer.
2026-04-09
First version: profile, supporters, payments, subscriptions, statistics and payouts.