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.
https://kreosus.com/api/v1curl "https://kreosus.com/api/v1/profile" \ -H "X-API-Key: $KREOSUS_API_KEY"
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.
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.
read | Read your profile, supporters, payments, subscriptions, statistics, payouts and posts. |
posts:write | Create, edit, publish and delete posts. |
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": "…" }
}
}400 | VALIDATION_FAILED, INVALID_FORMAT | A parameter or a body field is not valid. |
401 | AUTHENTICATION_REQUIRED | The key is missing or not valid. |
403 | INSUFFICIENT_SCOPE, PERMISSION_DENIED | The key lacks the permission, or the profile may not post. |
404 | RESOURCE_NOT_FOUND | The endpoint or the post does not exist, or the post is not yours. |
405 | INVALID_REQUEST | The endpoint does not accept this method. The Allow header names the one it accepts. |
409 | RESOURCE_CONFLICT | The post is already live. |
422 | IDEMPOTENCY_KEY_REUSED | The Idempotency-Key was used before with a different request. |
429 | RATE_LIMIT_EXCEEDED | Too many requests. Wait for the seconds in Retry-After. |
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 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.
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
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.
GET/api/v1/profile
Returns your profile information, support tiers, and active goals.
creator.idintegercreator.titlestringcreator.urlstringcreator.currencystringcreator.imagestring | nulltiers[].idintegertiers[].titlestringtiers[].descriptionstringtiers[].amountnumbertiers[].amount_minorintegertiers[].currencystringgoals[].idintegergoals[].targetintegergoals[].descriptionstringgoals[].imagestring | nullcurl "https://kreosus.com/api/v1/profile" \ -H "X-API-Key: $KREOSUS_API_KEY"
GET/api/v1/supporters
Everyone who has paid this profile, the largest total first.
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 20 | Rows per page. (max 100) |
search | string | - | Search by name or email |
from | string (date-time) | - | Start, a date or an RFC 3339 timestamp. |
to | string (date-time) | - | End, a date or an RFC 3339 timestamp. |
items[].idstringitems[].namestringitems[].emailstringitems[].total_amountnumberitems[].total_amount_minorintegeritems[].currencystringitems[].is_subscriberbooleanitems[].first_payment_datestringitems[].vat_countrystring | nullitems[].vat_percentnumbercurl "https://kreosus.com/api/v1/supporters?limit=10" \ -H "X-API-Key: $KREOSUS_API_KEY"
GET/api/v1/payments
Every payment to this profile, the newest first, with its fees and the net amount.
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 20 | Rows per page. (max 100) |
status | integer | - | 1 succeeded, 2 failed, 3 refunded, 4 reversed as fraud. One of: 1, 2, 3, 4 |
from | string (date-time) | - | Start, a date or an RFC 3339 timestamp. |
to | string (date-time) | - | End, a date or an RFC 3339 timestamp. |
items[].idstringitems[].supporter_namestringitems[].supporter_emailstringitems[].amountnumberitems[].amount_minorintegeritems[].currencystringitems[].vat_amountnumberitems[].vat_amount_minorintegeritems[].stripe_feenumberitems[].stripe_fee_minorintegeritems[].platform_feenumberitems[].platform_fee_minorintegeritems[].net_amountnumberitems[].net_amount_minorintegeritems[].statusintegeritems[].messagestring | nullitems[].is_subscriptionbooleanitems[].datestringcurl "https://kreosus.com/api/v1/payments?from=2026-10-01T00:00:00%2B03:00&status=1" \ -H "X-API-Key: $KREOSUS_API_KEY"
GET/api/v1/subscriptions
Monthly subscriptions to this profile.
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 20 | Rows per page. (max 100) |
status | integer | - | 1 active, 0 canceled. One of: 0, 1 |
items[].idstringitems[].supporter_namestringitems[].supporter_emailstringitems[].amountnumberitems[].amount_minorintegeritems[].currencystringitems[].statusintegeritems[].subscription_datestringitems[].last_payment_datestring | nullitems[].vat_countrystring | nullitems[].vat_percentnumberitems[].total_paidnumberitems[].total_paid_minorintegercurl "https://kreosus.com/api/v1/subscriptions?status=1" \ -H "X-API-Key: $KREOSUS_API_KEY"
GET/api/v1/statistics
Totals for a range of whole days, next to the all-time totals and the current balance.
| Name | Type | Default | Description |
|---|---|---|---|
from | string (date) | Last 30 days | First day (YYYY-MM-DD). |
to | string (date) | Today | Last day (YYYY-MM-DD). |
currencystringperiod.fromstringperiod.tostringtotal_amountnumbertotal_amount_minorintegertotal_amount_all_timenumbertotal_amount_all_time_minorintegertotal_transactionsintegertotal_transactions_all_timeintegernew_subscribersintegernew_subscribers_all_timeintegeractive_monthly_supportersintegerone_time_supportersintegeraccount_balancenumberaccount_balance_minorintegerstripe_feenumberstripe_fee_minorintegerplatform_feenumberplatform_fee_minorintegercurl "https://kreosus.com/api/v1/statistics?from=2026-01-01&to=2026-03-31" \ -H "X-API-Key: $KREOSUS_API_KEY"
GET/api/v1/payouts
Your payout requests, the newest first.
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 20 | Rows per page. (max 100) |
items[].idstringitems[].amountnumberitems[].amount_minorintegeritems[].currencystringitems[].statusintegeritems[].payout_typestring | nullitems[].create_datestringitems[].notestring | nullcurl "https://kreosus.com/api/v1/payouts?limit=50" \ -H "X-API-Key: $KREOSUS_API_KEY"
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.
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 20 | Rows per page. (max 100) |
status | string | live | Live posts or drafts. One of: live, draft |
items[].idintegeritems[].urlstringitems[].statusstringitems[].contentstringitems[].imagesstring[]items[].visibilitystringitems[].visibility_scheduleobject[] | nullitems[].locked_viewstringitems[].like_countintegeritems[].comment_countintegeritems[].created_atstringitems[].edited_atstring | nullcurl "https://kreosus.com/api/v1/posts?status=draft" \ -H "X-API-Key: $KREOSUS_API_KEY"
GET/api/v1/posts/{id}
One post or draft of yours, by the id in its address.
| Name | Type | Default | Description |
|---|---|---|---|
id | integer | - | The post id, as in the post's address. |
post.idintegerpost.urlstringpost.statusstringpost.contentstringpost.imagesstring[]post.visibilitystringpost.visibility_scheduleobject[] | nullpost.locked_viewstringpost.like_countintegerpost.comment_countintegerpost.created_atstringpost.edited_atstring | nullcurl "https://kreosus.com/api/v1/posts/1024" \ -H "X-API-Key: $KREOSUS_API_KEY"
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.
| Name | Type | Default | Description |
|---|---|---|---|
content | string | - | The text, up to 10,000 characters. Required unless you send images. |
visibility | string | public | Who can open the post. One of: public, active_supporters, all_supporters, custom |
schedule | array | - | 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_view | string | teaser_countdown | With visibility custom, what others see before their stage opens: teaser_countdown, teaser or hidden. |
images | array | - | 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. |
draft | boolean | false | true saves a draft instead of publishing. |
post.idintegerpost.urlstringpost.statusstringpost.contentstringpost.imagesstring[]post.visibilitystringpost.visibility_scheduleobject[] | nullpost.locked_viewstringpost.like_countintegerpost.comment_countintegerpost.created_atstringpost.edited_atstring | nullcurl -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"}'PATCH/api/v1/posts/{id}
Changes only the fields you send. A field you leave out keeps its value.
| Name | Type | Default | Description |
|---|---|---|---|
id | integer | - | The post id, as in the post's address. |
| Name | Type | Default | Description |
|---|---|---|---|
content | string | - | The text, up to 10,000 characters. Required unless you send images. |
visibility | string | - | Who can open the post. One of: public, active_supporters, all_supporters, custom |
schedule | array | - | 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_view | string | - | With visibility custom, what others see before their stage opens: teaser_countdown, teaser or hidden. |
images | array | - | 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_images | array | - | 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. |
post.idintegerpost.urlstringpost.statusstringpost.contentstringpost.imagesstring[]post.visibilitystringpost.visibility_scheduleobject[] | nullpost.locked_viewstringpost.like_countintegerpost.comment_countintegerpost.created_atstringpost.edited_atstring | nullcurl -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/api/v1/posts/{id}
Deletes a post or a draft. You cannot undo this.
| Name | Type | Default | Description |
|---|---|---|---|
id | integer | - | The post id, as in the post's address. |
idintegerdeletedbooleancurl -X DELETE "https://kreosus.com/api/v1/posts/1024" \ -H "X-API-Key: $KREOSUS_API_KEY"
POST/api/v1/posts/{id}/publish
Publishes a draft. A post that is already live gets 409.
| Name | Type | Default | Description |
|---|---|---|---|
id | integer | - | The post id, as in the post's address. |
post.idintegerpost.urlstringpost.statusstringpost.contentstringpost.imagesstring[]post.visibilitystringpost.visibility_scheduleobject[] | nullpost.locked_viewstringpost.like_countintegerpost.comment_countintegerpost.created_atstringpost.edited_atstring | nullcurl -X POST "https://kreosus.com/api/v1/posts/1024/publish" \ -H "X-API-Key: $KREOSUS_API_KEY" \ -H "Idempotency-Key: $(uuidgen)"
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.