API Guide
REST architecture, JSON-only responses, Bearer token auth. Every endpoint lives under /api/v1 and returns the same response envelope.
Your first request in four steps
There is no separate developer screen: your API identity is your own account.
-
Open your accountThe email and password you use in the panel are also your API credentials. You do not need to generate a separate key.
-
Get a tokenPOST your credentials to the signin endpoint; the response field data.token is your session token.
-
Add it to the headerCarry the token on every following request in the Authorization: Bearer header. No cookies are sent.
-
Read the envelopeLook at the result field in the body, not just the HTTP status: on ok you read data, on error you read error.code.
Code samples
The address below is derived from the domain this page is served from — you can copy it as is.
curl -X POST 'https://sosyalasistan.app/api/v1/signin' \
-H 'Content-Type: application/json' \
-d '{"mail": "[email protected]", "password": "your-password"}'{
"result": "ok",
"data": {
"token": "7f1c…",
"user": { "id": "7312904418291712", "mail": "[email protected]" }
}
}curl 'https://sosyalasistan.app/api/v1/me' \ -H 'Authorization: Bearer 7f1c…'
{
"result": "error",
"error": { "code": "UNAUTHENTICATED", "message": "Oturum yok" }
}Tokens live in Redis and are invalidated on sign-out; the signout endpoint accepts POST or DELETE.
Endpoint directory
Every live resource and the credential each one needs. The list is locked against the routing registry; only the router internals and one provider callback stay out.
| Endpoint | Identity |
|---|---|
| Identity and session Token creation, sign-out, and the session owner's own record. | |
| /api/v1/signin | no auth |
| /api/v1/signout | Bearer token |
| /api/v1/me | Bearer token |
| Account and team Users, companies, sub-users, and permissions. | |
| /api/v1/users | admin permission |
| /api/v1/companies | Bearer token |
| /api/v1/subUsers | Bearer token |
| /api/v1/permissions | Bearer token |
| /api/v1/companyImages | Bearer token |
| Social publishing Connected channels, posts, workflows, feeds, and reports. | |
| /api/v1/channels | Bearer token |
| /api/v1/posts | Bearer token |
| /api/v1/workflows | Bearer token |
| /api/v1/feeds | Bearer token |
| /api/v1/analytics | admin permission |
| Blog and content Blog posts, topics, personas, automation, and the knowledge base. | |
| /api/v1/blog | no auth write: admin permission |
| /api/v1/blogTopics | admin permission |
| /api/v1/blogPersonas | admin permission |
| /api/v1/blogAutomation | admin permission |
| /api/v1/blogReact | no auth |
| /api/v1/blogSubscribe | callback |
| /api/v1/blogTrending | no auth |
| /api/v1/knowledge | no auth |
| AI and files Generation requests, the assistant, and uploaded files. | |
| /api/v1/ai | Bearer token |
| /api/v1/aiCopilot | Bearer token |
| /api/v1/files | Bearer token |
| System and notifications Health checks, metrics, the audit log, notifications, and webhooks. | |
| /api/v1/health | no auth |
| /api/v1/metrics | operations credential |
| /api/v1/audit | admin permission |
| /api/v1/notifications | Bearer token |
| /api/v1/webhooks | callback |
| /api/v1/onboarding | Bearer token |
| /api/v1/support | Bearer token |
| /api/v1/sales | Bearer token write: admin permission |
| /api/v1/publishApproval | callback |
The verb mapping is the same for every resource: GET to list and read, POST to create, PUT to update, DELETE to remove. The response tells you which verbs a resource supports — an unsupported verb returns 405.
- no auth
- No header required; the request works as-is.
- Bearer token
- Send the data.token value from the POST /api/v1/signin response in the Authorization: Bearer … header.
- admin permission
- Requires an administrative identity and permission; a customer token does not pass — depending on the endpoint you get 401 or 403.
- operations credential
- Requires a separate monitoring credential (an X-Metrics-Token header or an allow-listed IP); a customer token does not pass, and with no configuration it returns 401.
- callback
- You are not the caller: the request is authenticated by the provider signature or by a single-use link sent in e-mail.
The envelope is the same for every resource: a successful response carries result: "ok" plus data, an error carries result: "error" plus error.code. Field lists are printed further down this page for the resources listed there; for the rest, the response to your own request is the authority on field names.
Error codes
error.code is always present in an error body. Branch on the codes you recognise and fall back to the HTTP status class for everything else: an endpoint may answer with a code that is more specific than the generic set below.
| Status | Error code | Meaning |
|---|---|---|
| 400 | INVALID_INPUT | The input format is invalid. |
| 401 | UNAUTHENTICATED | The token is missing, invalid, or expired. |
| 403 | FORBIDDEN | The identity is valid but you are not authorized for this record. |
| 404 | NOT_FOUND | The resource was not found. |
| 409 | ALREADY_EXISTS | The record already exists or the state conflicts. |
| 422 | VALIDATION_FAILED | Field validation failed; the detail is in error.fields. |
| 429 | RATE_LIMITED | The request limit was exceeded; wait before retrying. |
| 500 | INTERNAL_ERROR | An unexpected error on the server side. |
Endpoint-specific codes
These rows are not hand-written either: the code is read from the file that actually emits it and the status from the same map the server uses. Your very first request can already return a code that is not in the table above.
| Endpoint | Status | Error code | Meaning |
|---|---|---|---|
| /api/v1/signin | 401 | INVALID_CREDENTIALS | The e-mail and password did not match — sign-in answers with this code, not with UNAUTHENTICATED. |
| /api/v1/publishApproval | 400 | TOKEN_REQUIRED | The approval-link token is missing. This code is not in the status map, so it falls back to the default status. |
The table above is the generic set, not the whole list: 413 codes are mapped to a status in our code and further codes are produced without a mapping — an unmapped code comes back with the default 400. Handle a code you do not recognise by its HTTP status class, and never read a status as if it implied the generic code.
Example request bodies
These field lists are not hand-written: they are read from the validation group that filters the endpoint's own requests, so what the gate enforces is what you see.
/api/v1/signin rule group: signin
| Field | Required | Rules |
|---|---|---|
| yes | ||
| password | yes | string |
| google_authenticator | no | max:24 |
/api/v1/companies rule group: create
| Field | Required | Rules |
|---|---|---|
| title | no | max:160 |
| no | email max:255 | |
| phone | no | phone max:30 |
| address | no | max:500 |
| country | no | max:64 |
| timezone | no | max:64 |
| logo | no | max:2048 |
| color | no | max:32 |
| whatsapp_number | no | phone max:30 |
| sector | no | max:120 |
| size | no | max:32 |
| website_url | no | max:2048 |
| website | no | max:2048 |
| category_slug | no | max:120 |
| name | no | max:160 |
/api/v1/companies rule group: update
| Field | Required | Rules |
|---|---|---|
| title | no | max:160 |
| no | email max:255 | |
| phone | no | phone max:30 |
| address | no | max:500 |
| country | no | max:64 |
| timezone | no | max:64 |
| logo | no | max:2048 |
| color | no | max:32 |
| whatsapp_number | no | phone max:30 |
| sector | no | max:120 |
| size | no | max:32 |
| website_url | no | max:2048 |
| website | no | max:2048 |
| category_slug | no | max:120 |
/api/v1/posts rule group: create
| Field | Required | Rules |
|---|---|---|
| description | no | min:1 max:20000 |
| company_id | no | objectid |
| workflow_id | no | objectid |
| assign_id | no | objectid |
| format | no | string max:16 |
| content_type | no | string max:16 |
| media_urls | no | string max:8000 |
| video_asset_id | no | objectid |
| video_ai_generated | no | boolean |
| style_brief_id | no | objectid |
| client_token | no | string min:16 max:64 |
| platform_texts | no | string max:500000 |
| platform_media | no | string max:40000 |
| first_comment | no | string max:20000 |
| internal_title | no | string max:100 |
| internal_labels | no | string max:4000 |
| platform_settings | no | string max:4000 |
/api/v1/posts rule group: update
| Field | Required | Rules |
|---|---|---|
| description | no | max:20000 |
| company_id | no | objectid |
| workflow_id | no | objectid |
| assign_id | no | objectid |
| platform_texts | no | string max:500000 |
| platform_media | no | string max:40000 |
| first_comment | no | string max:20000 |
| internal_title | no | string max:100 |
| internal_labels | no | string max:4000 |
| platform_settings | no | string max:4000 |
Three resources are listed here for now; the field list for the remaining 30 has not been written yet. Rules appear in the validator's own notation: max:255 means at most 255 characters, in:a,b means only the listed values.
Verifying webhooks
Every delivery we send to your endpoint is signed. The headers below are printed from the constants in our code, not typed by hand.
| Header | Sample value |
|---|---|
| X-Sosyalasistan-Signature | v1=8f3c… |
| X-Sosyalasistan-Timestamp | 1758412800 |
| X-Sosyalasistan-Event | order.created |
| X-Sosyalasistan-Delivery | 901f2b… |
1758412800.{"event":"order.created",…}Verify like this: build the base string, compute HMAC-SHA256 with your subscription secret, prefix the lowercase hex with v1=, and compare in constant time. The timestamp sits inside the signature, so you can drop a stale delivery with your own abs(now − timestamp) threshold.
Frequently asked questions
-
How does versioning work?
The version travels in the address. Every API response returns the X-Min-Version and X-Latest-Version headers; today both are 1. -
Which header should carry the token?
The preferred form is Authorization: Bearer <token>. The server also accepts the X-Auth-Token header. External integrations should not rely on cookies; always send the token in a header. -
How are list endpoints paginated?
With the page and per_page query parameters; per_page defaults to 20 and is capped at 100. The page information comes back in the meta.pagination field. -
Why do record IDs come back as strings?
IDs are 64-bit integers and JavaScript loses precision at that width. They are therefore returned as strings in JSON; compare them as strings. -
What format do date fields use?
Every date field is a unix epoch integer in seconds. No textual date format is returned. -
Is the response always JSON?
Yes — every response, error paths included, is served with the application/json header. If you get HTML back, the request never reached the API; check the version prefix in the address.