Yallapp API referansı
Kendi web sitelerinden Yallapp API'sini sunan geliştiriciler için uygulamanızın çağırdığı her uç nokta. Adım adım rehberle başlayın, ardından bu referansı kullanın.
- 43 grupta 364 uç nokta, sürüm 1.0.0. Dosya OpenAPI 3.1 biçimindedir: her alan ve hata kodu için herhangi bir OpenAPI görüntüleyicide açın.
- Her yerde JSON, tarihler ISO 8601 UTC biçiminde, hatalar
{ code, message }olarak, listelerpageveper_pageile sayfalanır. - Oturum gerektiren çağrılar erişim belirteciyle birlikte
Authorization: Bearergönderir. Bkz. giriş akışı.
Aşağıdaki uç noktalar, alanlar ve örnekler, openapi.yaml dosyasında olduğu gibi İngilizcedir.
App
App configuration and the home screen.
- GET
/configGiriş gerekmezGet app configurationBranding, detected features and app rules set in the plugin settings. Called on every app start. - GET
/homeGiriş isteğe bağlıGet home screen sectionsSections in the order chosen in the plugin settings. Signed-out users get the public sections only. - GET
/strings/{language}Giriş gerekmezGet the owner's translations of the app's own texts in one languageServed by the Yallapp platform for every app, also those on WordPress or their own API: the app takes the address from `Config.languages.list[].strings.url` and never builds it. The pack has the owner's translations of the app's interface strings (the keys of the app's string files); the app shows the owner's string, else its own in that language, else English. Keep the last pack for offline use and download it again when `strings.version` in `/config` changes. Strings with placeholders use the same ICU format as the app's string files (`{name}`, `{count, plural, =1{…} other{…}}`), checked when the owner saves them. With `draft=1` and `X-Yallapp-Preview`, the app preview gets the Pages & look draft's strings.
Pages
Pages and bottom tabs built in the client dashboard.
- GET
/pagesGiriş isteğe bağlıGet the bottom tabs and the list of pagesThe navigation and pages the client published in the dashboard. When nothing is published yet, the server sends default pages and tabs for the app type, built from the site's content, so the answer is always a full navigation. Servers without pages (older plugins) return 404; the app then uses its built-in tabs and `/home`. The default pages follow `app_type`, the type of the app asking, so they match its screens from the first start. Without it the server uses the type the owner chose in the dashboard, else a guess from the site's plugins. Published pages don't depend on it. Send `If-None-Match` with the last `version` to get 304 when nothing changed. - GET
/pages/{slug}Giriş isteğe bağlıGet one page with its blocksBlocks in the order set in the dashboard, with lists (courses, products, services, posts, listings) already resolved. Blocks meant only for signed-in or signed-out users are filtered here. On iOS, blocks and items never carry purchase actions for paid content. 99.3 So are blocks the owner shows only between two dates, on the other platform or in other languages (than the one asked for with `Accept-Language`): the app never gets them. - POST
/forms/{slug}/{block_id}Giriş isteğe bağlıSend the answers to a form block by emailRoadmap 99.4. For a `form` block whose `send_to` is `email`: the server checks the answers against the published block (each field it knows, required fields filled, an email or phone that looks like one, a choice among its options, at most 2,000 characters each) and emails them to the business: the hosted API to the app's support email (else its owner's), the plugin to the support email it has from Yallapp (else the site's admin email). With a signed-in person's token, their name and email go with the answers. A form whose `send_to` is `chat` is sent by the app as a chat message instead (`POST /chat/conversations/{conversation_id}/messages`), which needs the person signed in. At most 5 answers a minute and 30 a day from one address, counting only answers that pass the checks.
Auth
Sign-in with a one-time code, tokens and sign-up.
- POST
/auth/otp/requestGiriş gerekmezSend a sign-in codeSends a 6-digit code to the phone number. Rate limited per number and per IP. On a WordPress site the plugin's settings choose who sends it: Yallapp (the default: the plugin makes the code and Yallapp delivers it on WhatsApp) or Digits (through the gateway set up in Digits; needs Digits' REST API add-on). Either way the account is the one with that number on the site, so people who signed up with Digits keep their accounts. The hosted API sends codes itself. Demo apps on the hosted API (the apps on yallapp.com.tr/demo) send no code: only the public demo number signs in, with the fixed code shown on that page. Any other number is refused with 403 `demo_number_only`. The demo number is limited per IP and per app, and works in demo apps only. The app preview in the Yallapp dashboard sends `preview: true`, and only when `Config.auth.preview_sign_in` is true. Such a request never sends a code: only the store review number signs in, with its fixed code. Any other number is refused with 403 `preview_review_number_only`. On the hosted API the store review number (and address) skips the per-number and per-IP limits, as reviewers sign in again and again, but is limited per app: 30 requests an hour for the number and the address together, then 429, so its fixed code can't be guessed. The plugin applies its usual limits to them. - POST
/auth/email/requestGiriş gerekmezSend a sign-in code by emailSends a 6-digit code to the email address, when `Config.auth.methods` has `email` and `Config.auth.email_sign_in` is `code` (the hosted API, and WordPress sites whose owner chose email codes). Same rules as phone codes: valid for 5 minutes, 5 attempts, a new code after a minute, limited per address and per IP. Verify it with `POST /auth/otp/verify`; an address with no account gets a `signup_token` (when sign-up from the app is allowed), and `POST /auth/register` then asks the same details as a phone sign-up (the email is already known). The email comes from the app's own email address when its owner set up their own email server in the Yallapp dashboard, else from Yallapp's address with the app's name. On WordPress the plugin makes the code and Yallapp delivers it; an account that can edit the site never signs in by code (it gets no email and the code is refused), so staff sign in on the website. Refused with 403 `method_not_allowed` when the app doesn't offer email sign-in. Demo apps sign in only by phone: 403 `demo_number_only`. Store review: the app's store review email address (for apps that sign in by email only, where reviewers can't use the review number) gets no email and signs in with the fixed review code. The app preview (`preview: true`) never sends a code: only the store review address signs in there, and any other address is refused with 403 `preview_review_number_only`. - POST
/auth/passwordGiriş gerekmezSign in with email and passwordFor servers whose `Config.auth.email_sign_in` is `password` (a WordPress site: its own accounts and passwords, checked by WordPress with the site's own rules and security plugins). Only existing accounts sign in here; people without an account sign up by phone or on the website (or, where the owner chose email codes, with `POST /auth/email/request`). A wrong address and a wrong password get the same 400 `invalid_credentials`, so the answer never tells whether an account exists. Limited per address and per IP. Devices and the device limit work as for phone sign-in. Refused with 403 `method_not_allowed` when the app doesn't offer email sign-in. In the app preview only the store review email address signs in; anything else gets 403 `preview_review_number_only`. Store review on WordPress: with the plugin's store review access on, its test email address signs in with the fixed code as the password, to the site's account with that address (made when there is none and sign-up is allowed; never an account that can edit the site). - POST
/auth/otp/verifyGiriş gerekmezVerify a sign-in codeVerifies a code from `POST /auth/otp/request` or `POST /auth/email/request`. Returns tokens for an existing account. For a phone number (or email address) with no account, returns a one-time `signup_token` to use with `POST /auth/register` (only when sign-up from the app is allowed). A new phone number is refused with 403 `app_user_limit` when the app has as many users as its plan allows (apps on the hosted API only; a WordPress site keeps its users and has no limit). The error carries `contact`, so the app can show how to reach the app's owner. People who already have an account keep signing in. - POST
/auth/registerGiriş gerekmezCreate an account after verificationFields required here follow the "Required at sign-up" plugin setting. Refused with 403 `app_user_limit` (with `contact`) when the app reached its plan's number of app users since the code was verified. - POST
/auth/refreshGiriş gerekmezRefresh tokensExchanges a refresh token for new tokens. The old refresh token stops working (rotation). Roadmap 68: also while the app is paused, so a signed-in person can still delete their account. - POST
/auth/logoutGiriş gerekirSign out this deviceAlso while the app is paused (roadmap 68).
Yallapp sign-in
For a client's own API whose owner chose "Yallapp signs people in" on the dashboard's App sign-in page (roadmap 34). Yallapp's hosted API for the app (`https://yallapp.com.tr/api/app/{app_id}`, `Config.auth.base_url`) serves `GET /config` (only its `auth` counts), the Auth endpoints (`/auth/otp/request`, `/auth/email/request`, `/auth/otp/verify`, `/auth/register`, `/auth/refresh`, `/auth/logout`), `GET`, `PATCH` and `DELETE /me`, `GET /me/devices` and `DELETE /me/devices/{device_id}`, with the sign-in methods, codes, store review access and device limit the owner set in Yallapp. The client's API serves everything else, including `/auth/checkout-link`, `PUT /me/push-token` and the notifications, and never the sign-in endpoints. The access token the app sends to the client's API is a JWT signed with ES256 (header `kid`) by a key pair of that app only, valid for 15 minutes. Claims: `iss` (`https://yallapp.com.tr`), `aud` (the app's ID), `sub` (the person's Yallapp user ID, a string), `phone` (E.164) and `email` only when proven by a code, `name`, `iat`, `exp`; `did` is Yallapp's own. The client's API checks the signature with the keys at `GET /.well-known/jwks.json`, and `iss`, `aud` and `exp`, then finds or creates its own user by `sub`, or by the proven phone or email. A missing, expired or invalid token answers 401, and the app refreshes it at Yallapp. Refresh tokens are opaque and work once; signing out, removing a device and deleting the account end them. Before deleting the account at Yallapp, the app calls the client's `DELETE /me`, so the client can delete its own data (404 or 405 when it keeps none). Roadmap 49: whatever that call answers (an error, no answer), the app then deletes the account at Yallapp, and Yallapp sends the client's API a signed notice, the `accountDeleted` webhook (`POST <API address>/webhooks/yallapp`), retried until the API answers 2xx. The client deletes the person by `sub` from either, whichever comes first.
- GET
/.well-known/jwks.jsonGiriş gerekmezGet the keys that sign access tokensServed only by Yallapp's hosted API, for an app on its own API that Yallapp signs people in to (404 otherwise): the app's public ES256 keys as a JSON Web Key Set, the one signing now first. When the owner replaces the key, the old one stays here for a day, so tokens it signed keep working until they expire. Cache it for up to 5 minutes (`Cache-Control`), and fetch it again when a token names a `kid` you don't have. - POST
/webhooks/yallappGiriş gerekmezA person deleted their account (sent to the client's API)Roadmap 49. Sent by Yallapp to an app on its own API that Yallapp signs people in to, at `<API address>/webhooks/yallapp`, when a person deletes their account in the app. Yallapp has deleted the account already; the client deletes what it keeps about `sub`. The body is a Security Event Token (RFC 8417): a JWT signed with the app's ES256 key, the one that signs its access tokens, checked with the same key set (`GET /.well-known/jwks.json`). Its header has `typ` `secevent+jwt`; its `aud` is `<app ID>:events`, never the access tokens' audience, so a notice never passes as an access token. Each try is signed again (`exp` 5 minutes after `iat`); `jti` stays the same on every try and is also in `Yallapp-Delivery`. Answer 2xx once it's received; repeats of a `jti` should answer 2xx too. A 5xx, 408, 425 or 429 answer, no answer within 10 seconds (5 for the first try) or an API that can't be reached is tried again after 1, 5 and 30 minutes, 2, 6, 12 and 24 hours (8 tries); any other answer stops it (404 or 405: the API doesn't take notices). Redirects aren't followed, and an address on a private network isn't called. The notices of the last 30 days and how each went are on App sign-in › For your developer, where a failed one can be sent again: it starts over with the same `jti` and the same tries.
Account
The signed-in student's profile, devices and push token.
- GET
/meGiriş gerekirGet the signed-in student - PATCH
/meGiriş gerekirUpdate profileIn a demo app everyone shares one account, which can't be changed (403 `feature_disabled`). - DELETE
/meGiriş gerekirDelete accountRequired by Apple and Google. Deletes or anonymises the WordPress user according to the site's policy and signs out every device. In a demo app (one shared account) it signs out this device only. Roadmap 68: it keeps working while the app is paused (its plan ended, or the owner's account is suspended), together with `POST /auth/refresh` and `POST /auth/logout`, on WordPress (plugin 0.4.6) and on the hosted API. The app offers it on its paused screen to someone who is signed in. - GET
/me/devicesGiriş gerekirList signed-in devicesIn a demo app, only the current device, and `limit` 0 (no limit). - DELETE
/me/devices/{device_id}Giriş gerekirSign out a device - PUT
/me/push-tokenGiriş gerekirRegister this device for push notifications - PUT
/me/cartGiriş gerekirSay how many items the cart holdsRoadmap 85.3: the app keeps the cart on the phone and says how many items it holds whenever that changes (0 once it's empty or ordered), so a shop on Yallapp's hosted API can send "You left something in your cart" once, 3 hours after the last change, when the owner hasn't turned it off. Nothing else about the cart is sent. Servers that don't need it answer 404, and the app doesn't ask again until it restarts.
Catalog
Categories, courses and course details.
- GET
/categoriesGiriş gerekmezList course categories - GET
/coursesGiriş isteğe bağlıList coursesPublished courses. A private course (WordPress's Private visibility, which the LMS uses to hide a course from its catalog) is never listed, not even to its students: they find it in `GET /me/courses` (50). - GET
/courses/{course_id}Giriş isteğe bağlıGet a courseA private course opens only for the students enrolled in it (bought, free or added by an administrator) and for whoever may read it on the website (its author, editors and administrators); anyone else gets `404 not_found`, as for a course that doesn't exist (50). A course with a password on the website (roadmap 62) shows in the catalog and opens as `password_protected`; see `CourseDetail`. - GET
/bundlesGiriş isteğe bağlıList course bundlesRoadmap 113.1. Courses managed in Yallapp: the academy's bundles (several courses sold together at one price the academy sets), in the academy's order. Only bundles with at least two published courses are listed (`features.bundles`). 404 on other sources. - GET
/bundles/{bundle_id}Giriş isteğe bağlıGet a course bundleRoadmap 113.1. The bundle's page in the app, its courses, and how it's bought on this platform (as a course is). - GET
/courses/{course_id}/curriculumGiriş isteğe bağlıGet a course's topics and itemsThe course's topics (Tutor LMS topics, LearnPress, LearnDash and MasterStudy sections, LifterLMS sections, Sensei modules...) in the order the LMS shows them, each with its lessons, quizzes, assignments and live classes in order. Items the student can't open are returned with `locked` set and no content. A private course's curriculum is `404 not_found` for whoever can't open the course (`GET /courses/{course_id}`, 50). A course with a password on the website (62) has no topics for a student who isn't in it.
Learning
Enrolled courses, lessons and progress.
- POST
/courses/{course_id}/restartGiriş gerekirStart a course againRoadmap 73. Clears the signed-in student's progress in the course (lessons completed, where each video stopped, and the quiz results the LMS keeps with the course, where it does) so they can take it again from the first lesson. Their enrollment, orders, reviews and questions stay. The app offers it ("Start the course again", after asking to confirm) on a finished course whose `CourseDetail.can_restart` is true, then opens the first lesson. `403 not_enrolled` for a student who isn't in the course, `403 restart_disabled` when `can_restart` is false (the owner turned it off for the course, or the LMS has no way to reset a student's progress). A certificate already issued is the LMS's: some keep it, some take it back. - GET
/me/coursesGiriş gerekirList the student's coursesPrivate courses the student is enrolled in are listed too (50). - GET
/me/wishlistGiriş gerekirList wishlisted coursesEmpty when the LMS has no wishlist (`features.wishlist` is false). Roadmap 62: a private course the student may open (enrolled in it, or may read it on the website) is listed like any other; one they can't open isn't. - PUT
/me/wishlist/{course_id}Giriş gerekirAdd a course to the wishlistFails with `feature_disabled` when the LMS has no wishlist (`features.wishlist` is false). A course the student can't open (a private one they aren't in) is `404 not_found` (62). - DELETE
/me/wishlist/{course_id}Giriş gerekirRemove a course from the wishlistFails with `feature_disabled` when the LMS has no wishlist (`features.wishlist` is false). - GET
/lessons/{lesson_id}Giriş isteğe bağlıGet a lessonFree-preview lessons are public; everything else requires enrollment. A lesson of a private course is `404 not_found`, free previews included, for whoever can't open the course (`GET /courses/{course_id}`, 50); so is a lesson of a course with a password on the website for a student who isn't in it (62). - PUT
/lessons/{lesson_id}/progressGiriş gerekirSave video position - POST
/lessons/{lesson_id}/completeGiriş gerekirMark a lesson as completeFails with `watch_requirement_not_met` when the site requires a share of the video to be watched first. - POST
/lessons/{lesson_id}/downloadGiriş gerekirLinks to save a lesson for offline viewingRoadmap 45. When `features.downloads` is true and the lesson's `offline.available` is true, the app asks for the lesson's files here and keeps them on the phone, encrypted, in its own storage. Each link is for this student only and works for a short time (`expires_at`): the app downloads at once, and asks again to resume a download after that. A lesson whose video plays from a video site (YouTube, Vimeo, Bunny Stream and other players) can't be saved: 409 `not_downloadable`, with `details.reason` as in `LessonOffline`. 403 `feature_disabled` when downloads are off for the app, 403 `not_enrolled` when the student can't open the lesson. - POST
/me/downloads/checkGiriş gerekirWhether the student may keep their saved lessonsRoadmap 45. The app sends the lessons it keeps for offline viewing when it starts with a connection. A lesson is `refused` when the student can't open it any more (their enrollment ended, was refunded or expired, or the lesson is gone); the app deletes it. The app also deletes everything when `downloads` is false (the owner turned downloads off or the plan no longer includes offline mode), and a lesson it hasn't been able to check for `keep_days` days. - GET
/me/subscriptionsGiriş gerekirList the student's memberships and subscriptions on the websiteRoadmap 90.3. When `features.memberships` is true: the student's current Paid Memberships Pro levels, WooCommerce Subscriptions (active, on hold or cancelled but running until the end of the paid period) and LearnDash groups. Managed on the website: the app shows them and never sells them (no purchase buttons on iOS). - GET
/me/certificatesGiriş gerekirList the student's certificatesEmpty when the LMS has no certificate feature active.
Quizzes
Quiz questions, attempts and results.
- GET
/quizzes/{quiz_id}Giriş gerekirGet quiz details and the student's attempts - POST
/quizzes/{quiz_id}/attemptsGiriş gerekirStart an attemptReturns the questions. Questions of a type the app can't show natively are returned with `render_mode` `webview` and a signed `webview_url`. - POST
/quizzes/{quiz_id}/attempts/{attempt_id}/submitGiriş gerekirSubmit answers - GET
/me/quiz-attemptsGiriş gerekirList the student's quiz results
Assignments
Roadmap 112.5. Assignments of courses managed in Yallapp, the student's work handed in and its grade, and the files sent with it.
- GET
/assignments/{assignment_id}Giriş gerekirAn assignment, with what the student handed in and its gradeRoadmap 112.5. For the course's students: `not_enrolled` (403) otherwise, `access_ended` (403) once their access ended, and `previous_not_done` (403) in a course taken in order until the item before it is done. - PUT
/assignments/{assignment_id}/submissionGiriş gerekirHand in an assignmentRoadmap 112.5. Some text, a photo or a PDF (sent with `/course-uploads`), or both. It can be handed in again until it's graded (also when the teacher sent it back, `returned`); then `already_graded` (409), roadmap 113.7 unless the assignment allows another try (`attempts_allowed`). After its due date it's marked `late`. The student is told in the app when it's graded or sent back (`assignment_graded`). - POST
/course-uploadsGiriş gerekirStart sending a file for an assignmentRoadmap 112.5. A photo (JPEG, PNG, WebP) up to 20 MB or a PDF up to 200 MB, by a student of one of the app's courses, into the academy's Media library (413 `storage_full` when its storage is full). Sent in chunks to `upload_url` as chat files are, then `/complete`; its `id` is the `upload_id` handed in. - POST
/course-uploads/{upload_id}/resumeGiriş gerekirA fresh link to carry on with an upload - POST
/course-uploads/{upload_id}/completeGiriş gerekirFinish an upload409 `incomplete` with the offset reached when part of the file is missing.
Live classes
Roadmap 112.5. Live classes of courses managed in Yallapp, with their meeting links and recordings.
- GET
/live-sessions/{live_id}Giriş gerekirA live class of one of the student's coursesRoadmap 112.5. `join_url` from 15 minutes before it starts until it ends; `recording_url` once it's over and the academy added one. For the course's students only (`not_enrolled`, 403). - GET
/me/live-sessionsGiriş gerekirThe live classes of the student's coursesRoadmap 112.5. `upcoming` (the default) gives those coming and on now, soonest first; `past` those over, latest first. Courses whose access ended are left out.
Commerce
Free enrollment, checkout hand-off and the student's orders.
- POST
/auth/checkout-linkGiriş gerekirGet a one-time signed-in checkout linkAndroid only, and only when the "Enroll now on Android" setting is on. The link signs the student in on the website and opens the checkout for the course. It works once and expires after 5 minutes. Roadmap 113.1: courses managed in Yallapp also sell bundles (`bundle_id` instead of `course_id`); paying enrols the student in every course of the bundle, and a course they already have stays theirs. A coupon that takes the whole price (100%) enrols them at once, without a payment: the answer has `enrolled` true with the courses, and its `url` shows the order as received (the app doesn't open it). - POST
/courses/{course_id}/couponGiriş gerekirCheck a coupon for a paid courseRoadmap 112.5. What the coupon takes off the course's price, or why it doesn't apply (`status` `invalid` with `message`). Up to 20 tries an hour. Roadmap 113.1 A coupon that takes the whole price applies (`free` true) and enrols the student without a payment when they check out with it. - GET
/me/pointsGiriş gerekirThe customer's loyalty points91.4 Loyalty points of shops without a website (`StoreConfig.loyalty`): the balance, what it is worth, and the latest changes, newest first. Points are earned when an order is completed (what was paid for the products, after coupons and points), used in the cart (`CartInput.use_points`), given back when a paid order that used them is cancelled or refunded, and taken back when an order that earned them is refunded or its return approved. `feature_disabled` (403) when the shop has no loyalty points. - GET
/me/ordersGiriş gerekirList the student's ordersOrders from the site's checkout (the LMS's own or WooCommerce, or the hosted API's payment page for shops without a website), newest first. Empty when the site doesn't sell (`features.orders`). The Academy app on iOS doesn't show orders, as it doesn't sell courses; Store apps show them on both platforms. 96.3 Apps without a website that sell tickets (Events), paid courses (Academy) or memberships (gyms' Bookings) list those orders here too, each with its `receipt_url`, and the app shows them under My orders (Events and gyms on both platforms). - GET
/me/orders/{order_id}Giriş gerekirGet one of the customer's ordersOne order of the signed-in customer, for following it: a restaurant's order shows `fulfilment` and `fulfilment_status`, which change as the kitchen works on it (received, preparing, ready, out for delivery, completed). Each change also reaches the customer as a notification, whose push `data` carries the `order_id`. 91.2 Shop orders too: a shipment with its tracking, completed, cancelled or refunded is an `order_update` notification (unless the phone turned `orders` off) that opens this; on Yallapp's hosted API, shops on Shopify and the other connected platforms too (their orders are looked at for 30 days after a checkout from the app, every 15 minutes). `not_found` for someone else's order. - POST
/me/orders/{order_id}/returnGiriş gerekirAsk to return an order91.3 The customer asks the shop to take back an order it completed, within `StoreConfig.returns.days` (`Order.can_return`). The shop approves or declines it (with a reply) on its side; the customer is told with an `order_update` notification. Once asked, it can't be asked again (`conflict`); `forbidden` when the shop takes no returns from the app or the order can't be returned (not completed, too late, nothing physical). - POST
/bundles/{bundle_id}/couponGiriş gerekirCheck a coupon for a course bundleRoadmap 113.1. As `POST /courses/{course_id}/coupon`, for the bundle's price. Up to 20 tries an hour. - POST
/courses/{course_id}/enrollGiriş gerekirEnroll in a free courseA course with a password on the website (62) is `403 enrollment_disabled`: the student joins it on the website, where its password is entered. A course that needs a membership the student doesn't have (90.3, `CourseDetail.membership`) is `403 membership_required`.
Notifications
The notifications inbox.
- GET
/me/notificationsGiriş gerekirList the student's notificationsEvery notification the site sent to the student, newest first, whether or not push reached a device: to everyone, to the students of a course they're enrolled in, or to them alone. Kept for 90 days. - POST
/me/notifications/readGiriş gerekirMark every notification as read
Store billing
Roadmap 100. Courses and memberships bought in the app through Google Play Billing (Android) and the App Store's In-App Purchase (iOS), on Yallapp's hosted API for every app (`Config.store_billing.base_url`), as the stores ask for digital content. The app buys with the store's own SDK, then sends what the store gave it to `POST /billing/purchases`; Yallapp checks it with Google or Apple, records it once and gives the access where the course lives (the hosted API, the WordPress site through the plugin's `POST /billing/access`, Teachable or Thinkific through their enrolment APIs, or an app's own API through the `purchase.completed` notice). Refunds and ended subscriptions take it back. Roadmap 100.7: for an app on its own API, and for Teachable or Thinkific courses the platform's API doesn't price, the owner lists what the app sells on Dashboard › In-app purchases (each course's ID, title and price; each membership plan's ID, title, price, period and the course IDs it opens). Physical goods and services (shop products, orders, appointments, tickets to a venue, gym memberships, donations) keep their payment pages.
- POST
/billing/sessionGiriş gerekirA store billing token for the signed-in person (WordPress)Roadmap 100. Served by the Yallapp plugin (0.4.23 and later) when `Config.store_billing.session` is `site`, like `POST /chat/session`: the plugin gets the token from Yallapp (`POST /billing/sessions` in the platform API) for the signed-in person's WordPress user ID, display name and email. The app sends it as `Authorization: Bearer` to `Config.store_billing.base_url` until `expires_at` (one hour), and asks again on 401 `invalid_token`. - GET
/billing/productsGiriş isteğe bağlıWhat the app sells through the store on this platformRoadmap 100. Served by the hosted API at `Config.store_billing.base_url` for every app that sells in the app. The store product IDs of the paid courses and membership plans the owner sells, ready in the store. The app asks the store for each product's price in the person's currency and shows that price, never the site's. Public: no token needed. - POST
/billing/purchasesGiriş gerekirSend a purchase the store made, for the access it buysRoadmap 100. After the store's purchase flow says the purchase went through (Android: `purchased`; iOS: a verified transaction), the app sends the Google purchase token or the App Store's signed transaction. Yallapp checks it with the store (that it is this app's, for this product, paid and not refunded), records it once, gives the access (enrolment in the course, or the membership until `expires_at`) and acknowledges it with the store. Only then does the app finish the transaction with the store. Sending the same purchase again answers the same grant. A purchase the store marks pending (`status` `pending`, Android) gives nothing yet: Yallapp gives the access when Google tells it the purchase completed. `invalid_receipt` (400): not a valid purchase of this app's product. `purchase_in_use` (409): the purchase gives access to another account of this app already (a store account buys for one app account). `store_unavailable` (503): the store couldn't be reached; the app keeps the transaction unfinished and sends it again later (at the next start too). A purchase never acknowledged is refunded by Google after three days. - POST
/billing/purchases/restoreGiriş gerekirRestore purchasesRoadmap 100. "Restore purchases" (which Apple asks for): the app sends every purchase the store has for the person (Android: the purchase tokens of `queryPurchases`; iOS: the signed transactions of `Transaction.currentEntitlements`), and gets back the grants still active for this account. Purchases already in use by another account of the app are left out, never moved. - POST
/webhooks/yallappGiriş gerekmezA purchase in the store gives or takes access (sent to the client's API)Roadmap 100. Sent to an app on its own API that Yallapp signs people in to and that sells in the app, at the same address and signed the same way as `account.deleted` (a Security Event Token with the app's key), once Yallapp has checked a store purchase (`purchase.completed`), and when Google or Apple says it was refunded, revoked or ended (`purchase.revoked`) or renewed (`purchase.completed` again, with a later `expires_at`). The client gives or takes the access in its own system. Retries and `Yallapp-Delivery` are as for `account.deleted`; the delivery log, with "Send again", is on Dashboard › In-app purchases. Roadmap 100.7. `jti` (and `Yallapp-Delivery`) is the same for every try of one change of one purchase, and a new one for its next change (a renewal, a refund): give or take the access once per `jti`, and answer 2xx to repeats. Answer 2xx only once the access is given or taken: until a `purchase.completed` is delivered, the app hears `store_unavailable` and Yallapp doesn't acknowledge the purchase with Google (which refunds a purchase not acknowledged within three days). A notice still waiting for another try when the purchase changes again is dropped, never sent late: the newer notice says how things stand.
Chat
Roadmap 77. A person and the business (`direct`), and course groups in academies (`group`), on Yallapp's hosted API (`Config.chat.base_url`). People never talk to each other in private. Text, photos, videos, voice messages and files; files count in the app's storage and show in the business's Media library. Messages are kept a year, and longer when the business chooses; the business moderates (reports, blocking, deleting) and is responsible for what is said in its chat. Deleting one's account (`DELETE /me`, on the hosted API or a WordPress site) deletes the person's messages and files too.
- POST
/chat/sessionGiriş gerekirA chat token for the signed-in person (WordPress)Roadmap 77. Served by the Yallapp plugin only (0.4.9 and later): the chat lives on Yallapp, and a WordPress app's people sign in on the website, so the app asks its site for a chat token, which the plugin gets from Yallapp (`POST /chat/sessions` in the platform API) for the signed-in person: their WordPress user ID, display name and picture, and for an academy the courses they're enrolled in (their groups). The app then calls `Config.chat.base_url` with this token until `expires_at` (one hour), and asks again on 401 `invalid_token`. The hosted API and Yallapp sign-in don't need it: the app uses its own access token on `Config.chat.base_url` (`Config.chat.session` is `access_token` there). - GET
/chat/conversationsGiriş gerekirThe person's conversationsServed by the hosted API (`Config.chat.base_url`) for every app with chat. The conversation with the business (`kind: direct`) always comes first, made on the first call; then, in an academy that turned on course groups, the group of each course the person is enrolled in, most recent message first. Students never talk to each other in private. 113.6 In a school that allows messages, a teacher's conversations with a parent or a student (`kind: school`) follow, each with what the school allows in `school`: always a teacher and one parent or student, never two students or two parents. - GET
/chat/unreadGiriş gerekirHow many messages are unreadA cheap call for the badge, made when the app opens and comes back to the front. - GET
/chat/conversations/{conversation_id}/messagesGiriş gerekirMessages of a conversationNewest first with `before` (older pages), or oldest first with `after` (what arrived since the last message the app has). While the conversation is on screen the app asks with `after` every `poll_after_seconds` (3 while messages come, up to 15 when it's quiet); a push brings it back when the app isn't open. Messages a person blocked (`ChatMember.blocked`) are left out for them; a deleted message stays in place as `deleted: true` without its text or file. - POST
/chat/conversations/{conversation_id}/messagesGiriş gerekirSend a messageText (up to 4,000 characters), a finished upload (`upload_id`, from `POST /chat/uploads`), or both. `client_id` makes it safe to send again after a network error: the same `client_id` in the same conversation answers the message already made. Members the business blocked can read but not send (403 `chat_blocked`). 60 messages a minute per person. 113.6 In a `school` conversation the school's rules apply: 403 `conversation_closed` (the school closed it), `messages_not_allowed` (the school doesn't allow messages between them now), `teacher_first` (only teachers write first and this one hasn't), `attachments_not_allowed` (text only); 409 `outside_hours` outside the school's messaging hours, with when one can write again in `message` (the message isn't kept to be sent later). - POST
/chat/conversations/{conversation_id}/readGiriş gerekirMark a conversation read up to a message - GET
/chat/conversations/{conversation_id}/membersGiriş gerekirThe people in a groupNames and pictures only, never a phone number or an email address. The business's team shows as the business. - DELETE
/chat/messages/{message_id}Giriş gerekirDelete one's own messageDeleted for everyone, with its file. Others see that a message was deleted. - POST
/chat/messages/{message_id}/reportGiriş gerekirReport a message to the businessThe business sees the report in its dashboard and Yallapp Business and decides (delete the message, block the sender). Once per person and message; a second report answers 204 too. - PUT
/chat/members/{member_id}/blockGiriş gerekirStop seeing a member's messagesFor the person asking only, in every group. The business itself can't be blocked. - DELETE
/chat/members/{member_id}/blockGiriş gerekirSee a member's messages again - POST
/chat/uploadsGiriş gerekirStart sending a photo, video, voice message or fileChecks the type and size (pictures 20 MB, videos 200 MB, voice messages 20 MB, files 50 MB: PDF, Word, Excel, PowerPoint, text) and the app's storage, where chat files count with the Media library's (413 `storage_full`). The app sends the file in chunks to `upload_url` exactly as Yallapp Business does for Media (the media gate's protocol), then calls `/complete`, then sends the message with `upload_id`. An upload not sent in a message within a day is deleted. - POST
/chat/uploads/{upload_id}/resumeGiriş gerekirA fresh link to carry on with an upload - POST
/chat/uploads/{upload_id}/completeGiriş gerekirFinish an upload409 `incomplete` with the offset reached when part of the file is missing.
Community
Course reviews and questions and answers. On Yallapp's hosted API (roadmap 63) the business answers questions and may reply to a review (`Review.reply`) or hide it from others (`hidden`, shown to its author only). Roadmap 64: the business may also hide a question or a single answer (`CourseQuestion.hidden`, `QuestionAnswer.hidden`); a hidden one reaches its author only, never other students' lists or counts.
- GET
/courses/{course_id}/reviewsGiriş isteğe bağlıList a course's reviewsApproved reviews, newest first, with the rating summary. Signed-in students also get their own review, approved or not. - PUT
/courses/{course_id}/reviews/mineGiriş gerekirWrite or edit the student's reviewOne review per student and course. Enrolled students only. The site may hold it for approval (`status`). On Yallapp's hosted API (roadmap 63) a student writes or edits at most 20 reviews an hour in an app (429). - GET
/courses/{course_id}/questionsGiriş gerekirList a course's questionsQuestions students asked in the course, newest first, without their answers. Enrolled students only. A question the business hid (roadmap 64) is listed only for its author, with `hidden`, and isn't in `total` for others. Roadmap 113.7, pinned questions first; with `lesson_id`, a lesson's discussion only (404 when the lesson isn't in the course). - POST
/courses/{course_id}/questionsGiriş gerekirAsk a questionRoadmap 113.7. With `lesson_id`, under one of the course's lessons (its discussion). - GET
/questions/{question_id}Giriş gerekirGet a question with its answersA question the business hid (roadmap 64) is `404` to everyone but its author. Hidden answers are listed only for their author. - DELETE
/questions/{question_id}Giriş gerekirDelete a questionRoadmap 113.7. Its author, or the course's teacher (`CourseQuestionThread.can_delete`); its answers go with it. - PUT
/questions/{question_id}/pinGiriş gerekirPin a question, or unpin itRoadmap 113.7. The course's teacher (an academy with many teachers) pins a question first in its list; others get `not_teacher` (403). The academy pins from its dashboard. - DELETE
/questions/{question_id}/answers/{answer_id}Giriş gerekirDelete an answerRoadmap 113.7. Its author, or the course's teacher (`QuestionAnswer.can_delete`). - POST
/questions/{question_id}/answersGiriş gerekirReply to a question
Store
WooCommerce products, the cart and the checkout hand-off, for Store apps. The cart lives in the app; the order is placed on the site's own checkout, opened with a one-time link that fills the WooCommerce cart.
- GET
/store/homeGiriş isteğe bağlıGet the store home screen sectionsSections in the order chosen in the plugin settings. Only sections with content are returned. - GET
/store/categoriesGiriş gerekmezList product categoriesCategories with products, in the site's own order. `parent_id` builds the tree. - GET
/store/productsGiriş isteğe bağlıList productsPublished products the shop shows (catalog visibility "shop" or "shop and search"; "search" when `search` is given). - GET
/store/products/{product_id}Giriş isteğe bağlıGet a product - GET
/store/products/{product_id}/reviewsGiriş isteğe bağlıList a product's reviewsApproved reviews, newest first, with the rating summary. Fails with `feature_disabled` when the site has reviews turned off. - POST
/store/cartGiriş isteğe bağlıPrice the app's cartChecks the app's cart against the shop: current prices, stock and quantity limits. Nothing is saved. Shipping, taxes and coupons are added on the site's checkout. - POST
/store/checkout-linkGiriş isteğe bağlıGet a one-time checkout link for the cartOn WordPress, the link fills the site's WooCommerce cart with these lines, keeps the delivery date and gift message for the order, signs the customer in when they are signed in to the app, and opens the checkout; it works once. On the hosted API it opens the shop's checkout (Shopify's or another connected platform's, or our payment page for shops without a website). The link expires at `expires_at`. Signed-out customers get a link only when the shop allows guest checkout (`store.guest_checkout`). The checkout's last page has `order-received` or `thank_you` in its address, so the app knows the order was placed. A `coupon` (91.1) goes with the order: applied on the site's checkout (WooCommerce, or a connected platform's discount link) or on our payment page; one that doesn't apply any more is left out, never an error. With `use_points` (91.4, shops without a website), the signed-in customer's loyalty points take their value off the order on our payment page: as many as they have then, up to what the products cost after the coupon. They are used when the order is paid. Fails with `cart_changed` (409) when a line is no longer available, with `one_product_at_a_time` (409) when the shop's checkout can only take one product from the app (show the message), and with `purchase_not_allowed` (403) for digital products on iOS. Shops with `store.fulfilment` (restaurants and cafés) also take how the order is collected: `fulfilment` (`pickup` or `delivery`, one the shop offers), a `delivery_address` for delivery, and `scheduled_for` (one of the `slots` of `GET /store/order-times`; null or missing for "as soon as possible"). They fail with `kitchen_closed` (409) for an order as soon as possible while closed (show the message; a scheduled time may still work), `below_minimum` (409) when a delivery's subtotal is under `store.fulfilment.min_order`, `outside_delivery_area` (400) when the district isn't one of `store.fulfilment.delivery_areas`, and `invalid_request` (400) for a missing address or a time that isn't offered. The delivery fee is added to the order on our payment page. Prices, options and fees are always computed by the server. - GET
/store/order-timesGiriş isteğe bağlıWhen the kitchen takes orders nowFor shops with `store.fulfilment` (restaurants and cafés): whether they're open now, when they open or close next, whether an order "as soon as possible" can be placed now, and the times a scheduled order can be for. Computed in the shop's time zone at the time of the request, so the app asks again before checkout. `not_found` for shops without `store.fulfilment`.
Listings
Property listings, for Real estate apps: search with filters, a listing's details, favorites and messages to the office. Listings are managed in the Yallapp dashboard and served by the hosted API.
- GET
/listings/filtersGiriş isteğe bağlıGet the filters the search offersThe property types, places and ranges that exist in the office's listings, for the search screen's filters. - GET
/listingsGiriş isteğe bağlıSearch listingsPublished listings matching the filters. Signed-in users get `favorited` on each. - GET
/listings/{listing_id}Giriş isteğe bağlıGet a listing - POST
/listings/{listing_id}/inquiriesGiriş gerekirMessage the office about a listingStarts a conversation with the office, or adds to the user's open one about this listing. The office answers from the dashboard. - GET
/me/favoritesGiriş gerekirList the user's favorite listings - PUT
/me/favorites/{listing_id}Giriş gerekirAdd a listing to favorites - DELETE
/me/favorites/{listing_id}Giriş gerekirRemove a listing from favorites - GET
/me/inquiriesGiriş gerekirList the user's conversations with the office - GET
/me/inquiries/{inquiry_id}Giriş gerekirGet a conversation with its messagesMarks the office's messages as read. - POST
/me/inquiries/{inquiry_id}/messagesGiriş gerekirReply in a conversationWriting again reopens a conversation the office closed. Refused with 403 while the office has messages turned off.
News
Articles and categories, for News & blog apps (roadmap 23). Read-only and free to read: there is no paid content. WordPress serves the site's published posts (never private or password-protected ones); the hosted API serves articles written in the Yallapp dashboard or read from the site's RSS or Atom feed. An article's body is processed like lesson content (`render_mode`, `html` with embed placeholders, `embeds`).
- GET
/news/homeGiriş isteğe bağlıGet the news home screenFeatured articles (sticky posts on WordPress, featured in the dashboard; the newest on a feed), the latest articles (without repeating those) and a rail per category with articles, newest first. Empty lists are sent as empty arrays; rails without articles are left out. - GET
/news/categoriesGiriş isteğe bağlıList article categoriesCategories with published articles, in the site's own order. - GET
/news/articlesGiriş isteğe bağlıList articlesPublished articles, newest first (or in the order of `ids`). - GET
/news/articles/{article_id}Giriş isteğe bağlıGet an articleA published article with its body, processed like a lesson's content.
Events
Events and tickets, for Events & tickets apps (roadmap 28): events with their days and times, a venue or an online link, and ticket types. Free tickets are taken in the app; paid tickets are bought on the web payment page and issued only once the payment is confirmed. Each ticket has a random code and a signed QR payload, checked once at the door. Tickets to events at a venue are physical-world services, bought on Android and iOS; tickets to online-only events (no venue) are digital content, so iOS never offers buying them (free tickets work everywhere). Served by the hosted API.
- GET
/events/filtersGiriş isteğe bağlıGet the filters the events list offersThe categories of upcoming published events, with how many each has, and the days of the first and last upcoming event, for the list's filters and the calendar. - GET
/eventsGiriş isteğe bağlıList eventsPublished events, soonest first. Without `from`, events that haven't ended yet; a cancelled event stays in the list with `status: cancelled` until it would have ended. `from` and `to` are days in `EventsConfig.time_zone`, both included. - GET
/events/{event_id}Giriş isteğe bağlıGet an eventA published event with its description (processed like a lesson's content), pictures, venue, ticket types and what each ticket type's button does on this platform (`TicketType.action`). The online link is sent only to people holding a ticket. Signed-in users get `my_tickets`. - POST
/events/{event_id}/registerGiriş gerekirGet free ticketsFree ticket types only, on every platform. One ticket per name in `attendee_names` (the signed-in user's name for the rest), at most the ticket type's `max_per_order`. Places are taken atomically: fails with `sold_out` (409) when there aren't that many left, with `sales_ended` (409) after the ticket type's sales end or once the event started, with `event_cancelled` (409), and with `purchase_required` (403) for a paid ticket type. - POST
/events/checkout-linkGiriş gerekirGet a one-time link to buy paid ticketsHolds the places and opens the web payment page; the tickets are issued only once the payment is confirmed, and show in `GET /me/tickets` then. Places that aren't paid for are freed after the link expires. Fails with `purchase_not_allowed` (403) on iOS for online-only events (no venue: digital content) and when the organiser can't take card payments, and with `sold_out`, `sales_ended` or `event_cancelled` (409) like `register`. The payment page's last page has `order-received` in its address. - GET
/me/ticketsGiriş gerekirList the user's ticketsEvery ticket issued to the signed-in user, upcoming events first (soonest first), then past ones. The app keeps the last answer on the device, so tickets and their QR codes show without a connection. - POST
/me/tickets/{ticket_code}/cancelGiriş gerekirCancel one of the user's ticketsFree tickets only, when the organiser allows it (`EventsConfig.allow_cancel_hours`) and the event starts at least that many hours later; the place goes back on sale. Fails with `cancel_not_allowed` (403) otherwise, and with a conflict (409) for a ticket already used or cancelled. Paid tickets are refunded by the organiser.
Donations
Campaigns and donations, for Charity & donations apps (roadmap 29). Campaigns are managed in the Yallapp dashboard and served by the hosted API. The donation itself is paid on a web page, into the charity's own payment account: in the iPhone app always in the system browser (App Store guideline 3.2.2), on Android in the app or in the browser as `open_in` says. A donation counts towards a campaign only after the payment is confirmed.
- GET
/donations/campaignsGiriş isteğe bağlıList campaignsOpen campaigns first (featured ones first, then the newest), then campaigns that have ended. Drafts are never listed. - GET
/donations/campaigns/{campaign_id}Giriş isteğe bağlıGet a campaignAn open or closed campaign with its story, processed like an article's body, and its most recent donations (anonymous ones without a name). - POST
/donations/checkout-linkGiriş isteğe bağlıGet a one-time link to the donation pageStarts a donation and returns the page where the donor pays. The donor's name and email for the receipt are asked on that page. `open_in` is always `browser` when `platform` is `ios`: an iPhone app may not take donations itself (App Store guideline 3.2.2), so the app opens the link in the system browser, never in a web view inside the app, and words the button "Donate on our website". On Android it is `in_app` (Google Play's billing rules don't cover donations to charities; the app may still open it in the browser). Fails with `invalid_request` (400) for an amount under the campaign's `min_amount`, a custom amount the campaign doesn't allow, or `recurring: monthly` when `DonationsConfig.monthly` is false; with `campaign_closed` (409) when the campaign has ended; with `purchase_not_allowed` (403) on iOS when `DonationsConfig.ios_mode` is `off`; and with `donations_unavailable` (503) while the charity's payments aren't set up. - GET
/me/donationsGiriş gerekirList the signed-in user's donationsConfirmed donations started from the app while signed in, newest first, each with its receipt; 47 refunded ones too, with `refunded_total`.
Delivery
Roadmap 104. Delivery and courier companies, on Yallapp's hosted API. The customer sends a parcel: picks a service, the pickup and drop-off places, sees the price before sending, and pays cash to the courier or by card on the company's payment page (its own iyzico, Kashier, Fawaterk, Stripe or PayPal account), as the company allows (`DeliveryConfig.payment`). Delivery is a service in the physical world, so the card payment page opens in the app on Android and iOS alike. Prices follow the company's choice (`DeliveryConfig.pricing`): by distance (a straight line between the two places, so both need their coordinates), by the drop-off zone, or one fixed price; a service may add its own amount. The customer follows the shipment by its stages, or also sees the courier on a map while it is on the way when the company chose live tracking (`DeliveryConfig.tracking`).
- GET
/delivery/servicesGiriş isteğe bağlıList the company's delivery servicesThe services a customer can choose (same-day, express, documents...), in the company's order. Empty when the company has none; the shipment then has no service. - POST
/delivery/quoteGiriş isteğe bağlıPrice a shipment before sending itThe price of sending a parcel between two places with a service. Fails with `invalid_request` (400) when a place needs its coordinates (pricing by distance), the zone is missing or unknown (pricing by zone), or the places are further apart than the company delivers (`DeliveryConfig.max_distance_km`). - POST
/delivery/locateGiriş gerekirRead a place's coordinates from a maps linkFor a place shared as a Google Maps or Apple Maps link (a WhatsApp location, a short `maps.app.goo.gl` link): the coordinates it points at. Only links of those maps are followed. Fails with `invalid_request` (400) when the link has no coordinates. - POST
/delivery/shipmentsGiriş gerekirSend a parcelCreates the shipment at the price `POST /delivery/quote` gives for the same places and service. Paid in cash (`payment: cash`), it waits for a courier at once (`pending`). Paid by card (`payment: card`), it waits for the payment (`awaiting_payment`) and the answer has `pay_url`, the company's payment page, opened in the app on both platforms (a service in the physical world); it waits for a courier once the payment is confirmed. Fails with `invalid_request` (400) as the quote does, or for a payment method the company doesn't take (`DeliveryConfig.payment`); with `payments_unavailable` (503) for a card payment while the company's payments aren't set up. - GET
/me/shipmentsGiriş gerekirList the signed-in customer's shipmentsNewest first; `active` only those not yet delivered, failed or cancelled. - GET
/me/shipments/{shipment_id}Giriş gerekirFollow a shipmentThe shipment with its stages, its courier and, with live tracking while the courier has it (`picked_up`, `on_the_way`), where the courier is. The app asks again every 15 seconds or so while the screen is open and the shipment is active. - POST
/me/shipments/{shipment_id}/cancelGiriş gerekirCancel a shipmentOnly before a courier has it (`awaiting_payment`, `pending`). A shipment paid by card is refunded by the company. Fails with `not_cancellable` (409) once a courier has it. - POST
/me/shipments/{shipment_id}/payGiriş gerekirGet a new payment link for a shipmentFor a shipment still `awaiting_payment`, when the first link was closed or ran out. Fails with `not_payable` (409) for any other.
Courier
Roadmap 104. The courier's screens. A courier is a person the company added on the dashboard's Couriers page by mobile number (or by email, for apps that sign in by email only); whoever signs in to the app with that number, proven by its code, is that courier (`User.roles` has `courier`). Every endpoint here answers `not_courier` (403) to anyone else, and to couriers the company paused. Couriers see only the jobs given to them, and, when the company lets couriers take jobs (`DeliveryConfig.assignment` is `open`), the jobs waiting for a courier, without the customer's and recipient's numbers until they take one. The courier's position is sent while the app is open and they have a job on the way, kept only while it is, and shown only to the customer of that job. Roadmap 112.3: these screens are in the company's second app made for couriers (build config `role: courier`) only; its customers' app has none. `DeliveryConfig.courier_app` says where they were before: `same`, in the company's app, shown only to couriers, which apps built before 112.3 still do; `bundle`, in the second app; `yallapp_courier`, in the shared Yallapp Courier app, while the company's app has none. Roadmap 104.8: `yallapp_courier` is Yallapp Courier, Yallapp's own app for the couriers of every delivery company that picks it in Delivery settings, with its own sign-in on the platform. The courier signs in to it once with the number (or email) the company added, picks the company, and Yallapp Courier opens a session at the company's hosted API for them: the same tokens `POST /auth/otp/verify` gives, used with these endpoints. Pushes to the company's couriers on Yallapp Courier's phones (`courier_job`) come from Yallapp Courier's own Firebase project, and their data also carries `app_id`, the company it is from. Such a session ends (401) once the company stops using Yallapp Courier, or pauses or removes the courier.
- GET
/courier/meGiriş gerekirThe signed-in courier - PATCH
/courier/meGiriş gerekirGo on or off dutyOnly couriers on duty are offered new jobs and told of them. - GET
/courier/jobsGiriş gerekirThe courier's jobs`active`: the courier's jobs not yet delivered, oldest first. `available`: shipments waiting for a courier, oldest first, only when the company lets couriers take jobs and the courier is on duty (else empty). `done`: the courier's delivered, failed and cancelled jobs of the last 30 days, newest first. - GET
/courier/jobs/{shipment_id}Giriş gerekirA jobOne of the courier's jobs, or one waiting for a courier that they could take. - POST
/courier/jobs/{shipment_id}/acceptGiriş gerekirTake a jobOnly when the company lets couriers take jobs and the courier is on duty. The first courier to take it gets it; the others get `job_taken` (409). - POST
/courier/jobs/{shipment_id}/statusGiriş gerekirMove a job to its next stageIn order: `picked_up` (the parcel is with the courier), `on_the_way`, then `delivered`, or `failed` when it couldn't be delivered (with a `note`). A cash shipment is paid once delivered: the courier collected `CourierJob.collect`. `released` gives a job that isn't picked up back to the company (to other couriers when they take jobs themselves). Fails with `invalid_status` (409) for any other step. - PUT
/courier/locationGiriş gerekirSend the courier's positionSent every 20 seconds or so while the app is open and the courier has a job picked up or on the way (no position is taken in the background). Kept only while they have one; answered 204 and dropped otherwise. At most one every 5 seconds is kept.
Food
Roadmap 105.1. Food delivery from many restaurants, on Yallapp's hosted API. The company adds its restaurants with their menus; the customer picks a restaurant (`GET /food/restaurants`), fills one restaurant's cart, sees the total before ordering (`POST /food/quote`: the dishes, their choices, the delivery fee by the company's delivery prices from the restaurant to the customer, and the restaurant's smallest order), and orders, paying cash to the courier or by card on the company's payment page as the company allows (`FoodConfig.payment`). The restaurant accepts the order with the time it needs, or turns it down; the company's couriers deliver it (the Courier tag: each order's delivery is a courier job, picked up at the restaurant). The customer follows the order's stages, and the courier on a map while it is on the way when the company chose live tracking, then rates the restaurant and the courier.
- GET
/food/restaurantsGiriş isteğe bağlıList the restaurantsThe company's active restaurants: open ones first, then by `sort`. With the customer's position (`lat`, `lng`) each has its distance and whether it delivers there (`delivers`); restaurants that don't are listed last. `cuisine` keeps one cuisine (`FoodConfig.cuisines`), `q` searches restaurant and dish names. - GET
/food/restaurants/{restaurant_id}Giriş isteğe bağlıA restaurant with its menuThe restaurant and its menu in sections, the company's order. Sold-out dishes are listed with `available` false. - POST
/food/quoteGiriş isteğe bağlıPrice one restaurant's cartThe cart's lines with their prices, the subtotal, the delivery fee to the drop-off and the total. Fails with `invalid_request` (400) for a dish or choice the restaurant doesn't have (or a missing required choice), with `unavailable_item` (409) for a sold-out dish (`field` names the line), `restaurant_closed` (409) while the restaurant is closed or busy, `below_minimum` (409) under the restaurant's smallest order, and `outside_delivery_area` (400) when the restaurant doesn't deliver to the drop-off. - POST
/food/ordersGiriş gerekirOrder from a restaurantPlaces the order at the prices `POST /food/quote` gives for the same cart. Paid in cash, it goes to the restaurant at once (`placed`). Paid by card, it waits for the payment (`awaiting_payment`) and the answer has `pay_url`, the company's payment page, opened in the app on both platforms (food delivered is a service in the physical world); it goes to the restaurant once the payment is confirmed. Fails as the quote does, for a payment method the company doesn't take, and with `payments_unavailable` (503) for a card payment while the company's payments aren't set up. - GET
/me/food-ordersGiriş gerekirList the signed-in customer's food ordersNewest first; `active` only those not yet delivered, turned down or cancelled. - GET
/me/food-orders/{order_id}Giriş gerekirFollow a food orderThe order with its stages, the courier and, with live tracking while the courier has it (`picked_up`, `on_the_way`), where the courier is. The app asks again every 15 seconds or so while the screen is open and the order is active. - POST
/me/food-orders/{order_id}/cancelGiriş gerekirCancel a food orderOnly before the restaurant accepts it (`awaiting_payment` or `placed`); then `not_cancellable` (409). A card payment already made is refunded by the company. - POST
/me/food-orders/{order_id}/payGiriş gerekirA new payment page for an order waiting for its payment - PUT
/me/food-orders/{order_id}/ratingGiriş gerekirRate a delivered orderOnce, after it is delivered, within 7 days. The restaurant's rating counts in its average; the courier's is seen by the company.
Restaurant
Roadmap 105.1. A restaurant's screens. Its staff are people the company added to the restaurant on the dashboard by mobile number (or by email, for apps that sign in by email only); whoever signs in with that number, proven by its code, works for that restaurant (`User.roles` has `restaurant`). Every endpoint here answers `not_restaurant` (403) to anyone else, and to staff of a restaurant the company paused. Staff see their restaurant's orders only: new ones (with a push and a sound), accept each with the minutes it needs or turn it down with a reason, mark it ready for the courier, and set the restaurant busy for a while (no new orders) or open again.
- GET
/restaurant/meGiriş gerekirThe restaurant the signed-in person works for - PATCH
/restaurant/meGiriş gerekirBusy for a while, or open again`busy_minutes` 15 to 240 stops new orders for that long; 0 opens again. Opening hours still apply. - GET
/restaurant/ordersGiriş gerekirThe restaurant's orders`new`: waiting to be accepted, oldest first; `active`: accepted and not yet picked up, the soonest ready first; `done`: picked up, delivered, turned down or cancelled in the last 2 days, newest first. - GET
/restaurant/orders/{order_id}Giriş gerekirOne of the restaurant's orders - POST
/restaurant/orders/{order_id}/acceptGiriş gerekirAccept a new order with the minutes it needsFrom `placed` only; then `invalid_status` (409). The customer is told when it will be ready, and, when the company makes courier jobs on acceptance (`FoodConfig.courier_job`), a courier job is made at once. - POST
/restaurant/orders/{order_id}/rejectGiriş gerekirTurn down a new orderFrom `placed` only. The customer is told the reason; a card payment is refunded by the company. - POST
/restaurant/orders/{order_id}/readyGiriş gerekirThe order is ready for the courierFrom `accepted` only. Makes the courier job when the company makes them once orders are ready (`FoodConfig.courier_job`).
Market
Roadmap 115. The marketplace: many restaurants, supermarkets and shops in one app (a Yallapp Studio project with the `marketplace` plan feature, growing 105.1's food delivery). Vendors are of three kinds (`restaurant`, `grocery`, `shop`); restaurants have menus, groceries and shops have departments, aisles and products, some sold by weight. The customer finds vendors (`GET /market/vendors`, `GET /market/search`), fills one vendor's cart, sees every line of the price before ordering (`POST /market/quote`: items before and after their discounts, the coupon, the club's discount, delivery, taxes, the wallet), and orders for now or for later, delivered or to pick up, paying cash, by card, with Apple Pay or Google Pay (`MarketConfig.payment`), or partly from the wallet. Each vendor chooses who delivers: the app's couriers (the order is followed on the map) or its own drivers (followed by its stages; on the map when the driver uses the courier app). Every amount is in `MarketConfig.currency`, as a decimal string with that currency's decimals (`MarketConfig.currency_decimals`), and formatted. Vendors never reach customers and customers never reach vendors: no vendor's phone, email or chat is ever shown, and support is the app's owner's (the Chat tag, and complaints here). A complaint can be opened on an order from when it is placed until 48 hours after it arrives; the owner's support team decides it.
- GET
/market/vendorsGiriş isteğe bağlıList the vendorsThe app's active vendors: open ones first, then by `sort`. With the customer's position (`lat`, `lng`) each has its distance, delivery time and fee, and whether it delivers there; vendors that don't are listed last (they can still be ordered from for pick-up when they allow it). `kind` keeps one kind, `category` one cuisine or department (`MarketConfig.categories`), `q` searches vendors and their items, and the flags keep vendors with free delivery, an offer, the club's benefit or pick-up. - GET
/market/vendors/{vendor_id}Giriş isteğe bağlıA vendorThe vendor with its hours, delivery terms, offers and the sections of what it sells: a restaurant's menu sections with their dishes, or a grocery's or shop's departments with their aisles (products come from `GET /market/vendors/{vendor_id}/products`, as there may be many). - GET
/market/vendors/{vendor_id}/productsGiriş isteğe bağlıA vendor's productsPaged, in the vendor's order. `department` and `aisle` keep one, `q` searches names, brands and descriptions, `barcode` finds one by its barcode or SKU. Out-of-stock products are listed with `in_stock` false after the others. - GET
/market/searchGiriş isteğe bağlıSearch vendors and what they sellVendors whose name or category matches, and the best matching dishes and products with their vendor, near the customer when `lat` and `lng` are given. - GET
/market/offersGiriş isteğe bağlıOffers and coupons the customer can useThe app's public offers (with their vendor, or the whole app), and, signed in, the coupons the customer holds (given after a complaint, or for a first order). Codes the owner keeps private are not listed. - POST
/market/quoteGiriş isteğe bağlıPrice one vendor's cartEvery line of the price, as the order would be charged: each item before and after its discount, the coupon (and whether the owner or the vendor pays it), the club's discount, the delivery fee (the app's courier prices or the vendor's own), taxes added or included, the wallet's part, and the total. Fails with `invalid_request` (400) for an item or choice the vendor doesn't have, `unavailable_item` (409) for one out of stock (`field` names the line), `vendor_closed` (409) while the vendor is closed, busy or paused (unless `scheduled_for` is within its hours), `below_minimum` (409) under the vendor's smallest order, `quantity_limit` (409) outside its fewest or most items, `outside_delivery_area` (400), `pickup_unavailable` (409), and `coupon_invalid` (409) with the reason in `message`. - POST
/market/ordersGiriş gerekirOrder from a vendorPlaces the order at the prices `POST /market/quote` gives for the same cart (the answer's `quote_id` keeps them for 10 minutes). Paid in cash, or fully from the wallet, it goes to the vendor at once (`placed`; a scheduled order at its time). Paid by card, Apple Pay or Google Pay, it waits for the payment (`awaiting_payment`): the answer has `pay_url`, the gateway's payment page (which offers Apple Pay and Google Pay where the gateway does), opened in the app on both platforms (food and goods delivered are services in the physical world, not in-app purchases); it goes to the vendor once the payment is confirmed. Fails as the quote does, with `quote_expired` (409), for a payment method the app doesn't take, and with `payments_unavailable` (503) while the app's payments aren't set up. - GET
/me/market-ordersGiriş gerekirThe signed-in customer's ordersNewest first; `active` keeps those not yet delivered, picked up, turned down or cancelled, scheduled ones included. - GET
/me/market-orders/{order_id}Giriş gerekirOne of the customer's ordersThe order with its stages, and `tracking` while it is on its way. - POST
/me/market-orders/{order_id}/cancelGiriş gerekirCancel an orderUntil the vendor accepts it (a scheduled order until 30 minutes before its time). An online payment is refunded to it, a wallet part to the wallet. `not_cancellable` (409) after that; the customer can open a complaint instead. - POST
/me/market-orders/{order_id}/payGiriş gerekirA new payment page for an order waiting for its payment - POST
/me/market-orders/{order_id}/ratingGiriş gerekirRate a delivered or picked-up orderOnce, within 7 days. The vendor (and the courier, when the app's courier delivered it), 1 to 5, with words for the vendor. - POST
/me/market-orders/{order_id}/complaintsGiriş gerekirOpen a complaint on an orderFrom when the order is placed until 48 hours after it was delivered or picked up (`complaint_closed` (409) after that); one open complaint per order at a time (`complaint_open` (409)). Photos are added next with `POST /me/complaints/{complaint_id}/photos`. - GET
/me/complaintsGiriş gerekirThe customer's complaintsNewest first. - GET
/me/complaints/{complaint_id}Giriş gerekirA complaint with its messages and decision - POST
/me/complaints/{complaint_id}/messagesGiriş gerekirWrite to support on a complaintUntil it is closed (`complaint_closed` (409)). - POST
/me/complaints/{complaint_id}/photosGiriş gerekirAdd a photo to a complaintA photo sent with `/market/uploads` (JPEG, PNG or WebP up to 8 MB) joins an open complaint, at most 5 a complaint (`too_many_photos` (409); a closed complaint is `complaint_closed` (409)). Seen by support and, when support shares it, by the vendor. - POST
/market/uploadsGiriş gerekirStart sending a complaint photoA photo (JPEG, PNG or WebP) up to 8 MB, by a customer with a complaint still open (`no_complaint` (403) otherwise), into the app's Media library (413 `storage_full` when its storage is full). Sent in chunks to `upload_url` as chat files are, then `/complete`, then added with `/me/complaints/{complaint_id}/photos`. - POST
/market/uploads/{upload_id}/resumeGiriş gerekirA fresh link to carry on with a complaint photo - POST
/market/uploads/{upload_id}/completeGiriş gerekirFinish sending a complaint photo409 `incomplete` with the offset reached when part of the photo is missing. - GET
/me/walletGiriş gerekirThe customer's walletIts balance and history (refunds, credit from support, paid orders). The wallet is in `MarketConfig.currency` and can't be withdrawn as cash. - GET
/market/clubGiriş isteğe bağlıThe delivery club's plansThe app owner's club plans (monthly or yearly, at the owner's prices) and their benefits; `MarketConfig.club` false when the app has no club (404). - GET
/me/clubGiriş gerekirThe customer's club membershipNull `membership` when not a member. - POST
/me/clubGiriş gerekirJoin the clubStarts a membership on a plan, waiting for its payment (`pay_url`, card, Apple Pay or Google Pay on the gateway's page); the membership is active once the payment is confirmed. A new period is paid the same way, by joining again once the paid period ends, or by a cancelled member taking the same plan back. `already_member` (409) while a period is paid. - POST
/me/club/cancelGiriş gerekirStop the club from renewingThe membership stays until the end of the period it is paid for.
Vendor
Roadmap 115. The vendor app's screens (and the vendor's web panel on Yallapp's site, which serves the same data). A vendor's staff are the people the app's owner or the vendor added by mobile number or email; whoever signs in with it works for that vendor (`User.roles` has `vendor`). Every endpoint here answers `not_vendor` (403) to anyone else and to staff of a vendor the owner paused. Staff see their vendor's orders only, with the customer's first name and the order's lines; a vendor that delivers itself also gets the drop-off address and the customer's notes for the door, never a phone number (its driver asks the app's support, which reaches the customer). Staff accept an order with the minutes it needs or turn it down, pick a grocery order (weighed amounts, substitutes, missing items), mark it ready, hand it over, and, when delivering themselves, mark it on the way and delivered; they set the vendor busy or paused, and items in or out of stock.
- GET
/vendor/meGiriş gerekirThe vendor, as its staff see it - PATCH
/vendor/meGiriş gerekirSet the vendor busy, paused or taking orders`busy_minutes` stops new orders for that long (scheduled ones still come); `paused` true stops them until set false again. Staff with the `orders` role or more. - GET
/vendor/ordersGiriş gerekirThe vendor's orders`status` `new` (waiting for an answer, oldest first), `active` (accepted, not yet handed over or delivered), `scheduled`, or `done` (the last 7 days, newest first). - GET
/vendor/orders/{order_id}Giriş gerekirOne of the vendor's orders - POST
/vendor/orders/{order_id}/acceptGiriş gerekirAccept an order with the minutes it needs - POST
/vendor/orders/{order_id}/rejectGiriş gerekirTurn an order downThe customer is told and refunded in full (to the payment, or the wallet for its wallet part). - PATCH
/vendor/orders/{order_id}/linesGiriş gerekirPick a grocery or shop orderFor each line: the amount picked (a weighed amount for items sold by weight, within the customer's agreed limit, `MarketConfig.weight_tolerance_percent`), a substitute (only as the customer allowed, `MarketOrderLine.if_missing`), or missing. The order's total is worked out again (a lower total refunds the difference when it was paid online; it is never charged more than the customer agreed). Accepted orders only. - POST
/vendor/orders/{order_id}/readyGiriş gerekirThe order is readyFor the app's courier or the vendor's driver to take it, or for the customer to pick up (told with the pick-up code). - POST
/vendor/orders/{order_id}/handoverGiriş gerekirHand the order over`to` `driver`: the vendor's own driver took it (`on_the_way`); `delivered`: the vendor's driver delivered it; `customer`: picked up, with the customer's 4-digit `pickup_code` (`wrong_code` (409)). With the app's couriers, the courier's own screens move the order instead (`invalid_request` (400) here). - GET
/vendor/productsGiriş gerekirThe vendor's items, for stockDishes or products, paged; `out_of_stock` keeps those out; `q` and `barcode` find them. - PATCH
/vendor/products/{product_id}Giriş gerekirPut an item in or out of stock`in_stock`, and for products that count it, `stock` (out of stock at 0 by itself).
Rides
Roadmap 105.2. Ride hailing, on Yallapp's hosted API. The rider sets the pickup and the drop-off, sees each of the company's vehicle types with its fare (`POST /rides/estimate`) and asks for a ride now or, when the company allows it, at a later time (`POST /rides`). The company's drivers get the request one at a time, nearest first, each with a few seconds to accept (`RidesConfig.matching`), or the company gives it to a driver from the dashboard. The rider follows the driver to the pickup and along the ride on the map, can share the ride with a link, call the company's emergency number, cancel (with the company's fee once the free minutes after a driver accepted are over), pay the fare in cash to the driver or by card on the company's payment page, and rate the driver. Fares, fees and the company's share are the company's: Yallapp sets none of them.
- POST
/rides/estimateGiriş gerekirThe fare of each vehicle type between two placesBoth places need their coordinates. The distance and time are estimated from the straight line between them (the road is taken as a third longer, at the company's usual speed), so the fare is `upfront` (the one the rider pays, plus waiting) or an estimate when the company charges what the meter says (`RidesConfig.fare_mode`). Vehicle types without an online driver nearby are listed with `available` false (asking for one still waits for a driver). Fails with `outside_service_area` (400) when the pickup is outside the company's areas, with `invalid_request` (400) for a place without coordinates or an unknown promo code (`field` names it), and with `rides_unavailable` (503) while the company has no vehicle type with fares. - POST
/ridesGiriş gerekirAsk for a rideAt the fare `POST /rides/estimate` gives for the same places, vehicle type and promo code. Now: it is offered to the drivers at once (`searching`). Later (`scheduled_at`, when `RidesConfig.scheduling` allows it, 30 minutes to 7 days ahead): it waits (`scheduled`) and is offered 15 minutes before. A card ride is paid once it ends, on the company's payment page (`POST /me/rides/{ride_id}/pay`). An unpaid cancellation fee from the rider's last ride is added to this one's fare. Fails as the estimate does; with `ride_in_progress` (409) while the rider has another ride that isn't over (scheduled ones aside); with `invalid_request` (400) for a payment method the company doesn't take or a time it doesn't allow; with `payments_unavailable` (503) for a card ride while the company's payments aren't set up. - GET
/me/ridesGiriş gerekirList the signed-in rider's ridesNewest first; `active` only those not yet completed or cancelled (scheduled ones too). - GET
/me/rides/{ride_id}Giriş gerekirFollow a rideThe ride with its stages, its driver and car and, from the driver's accepting until the ride ends, where the driver is. The app asks again every 5 seconds or so while the screen is open and the ride is active. - POST
/me/rides/{ride_id}/cancelGiriş gerekirCancel a rideBefore it starts (`scheduled`, `searching`, `accepted`, `arrived`); then `not_cancellable` (409). Free while no driver has it, and for `RidesConfig.cancellation.free_minutes` after a driver accepted; after that the company's fee (`cancellation.fee`) is charged, added to the rider's next ride. - POST
/me/rides/{ride_id}/payGiriş gerekirPay a card rideFor a card ride `completed` and not paid yet, the company's payment page (good for 60 minutes), opened in the app on both platforms (a service in the physical world). Fails with `not_payable` (409) for any other. - POST
/me/rides/{ride_id}/ratingGiriş gerekirRate the driverOnce, after the ride is completed, within 7 days; then `already_rated` or `not_ratable` (409). The rating counts in the driver's average; the comment is seen by the company.
Driver
Roadmap 105.2. The driver's screens. A driver is a person the company added on the dashboard's Drivers page by mobile number (or by email, for apps that sign in by email only), with their car; whoever signs in with that number, proven by its code, is that driver (`User.roles` has `driver`). Every endpoint here answers `not_driver` (403) to anyone else, and to drivers the company suspended. A driver goes online to get ride requests; while online, the app sends the driver's position (only while it is open), which is used to offer rides nearest first and shown to the rider of their ride; it is dropped when they go offline. The rider's number is shown once the driver accepted, until the ride ends. Roadmap 112.3: these screens are in the company's second app made for drivers (build config `role: driver`) only; its riders' app has none. `RidesConfig.driver_app` says where they were before: `same`, in the company's app, shown only to drivers, which apps built before 112.3 still do; `bundle`, in the second app.
- GET
/driver/meGiriş gerekirThe signed-in driver - PATCH
/driver/meGiriş gerekirGo online or offlineOnly online drivers are offered rides. Going offline drops their position; it is refused with `ride_in_progress` (409) while they have a ride that isn't over. - PUT
/driver/locationGiriş gerekirSend the driver's positionSent every 5 to 10 seconds while the app is open and the driver is online (no position is taken in the background). Kept only while they are online; answered 204 and dropped otherwise. At most one every 3 seconds is kept. During a ride, the positions also measure the distance the meter charges (`RidesConfig.fare_mode` `metered`). - GET
/driver/offersGiriş gerekirThe ride offered to the driver nowA ride offered to this driver, with when the offer ends (`offer_expires_at`); at most one at a time; empty when none is or the driver is offline. The app asks every 3 seconds or so while online and free, and on a `ride_offer` push. The rider's number is not shown until the driver accepts. - GET
/driver/ridesGiriş gerekirThe driver's rides`active`: the driver's ride that isn't over (at most one). `done`: their completed and cancelled rides of the last 30 days, newest first. - GET
/driver/rides/{ride_id}Giriş gerekirA rideOne of the driver's rides, or the one offered to them now. - POST
/driver/rides/{ride_id}/acceptGiriş gerekirAccept the ride offeredWhile it is offered to this driver; then `offer_expired` (409). The rider is told the driver is on the way. - POST
/driver/rides/{ride_id}/declineGiriş gerekirLet the ride go to the next driverThe offer ends at once and isn't made to this driver again. Answered 204 also when it had already ended. - POST
/driver/rides/{ride_id}/statusGiriş gerekirMove a ride to its next stageIn order: `arrived` at the pickup, `in_progress` once the rider is in (with the rider's `pin` when `RidesConfig.pin` is true; a wrong one is `wrong_pin`, 400), then `completed` at the drop-off, which works out the final fare (shown on the answer's `fare`); a cash ride is paid once completed: the driver collected `collect`. `cancelled` before the ride starts, with a `reason`: `rider_no_show` (only `RidesConfig.cancellation.no_show_minutes` after arriving; the rider is charged the cancellation fee) or `other` (the ride goes back to the next driver). Fails with `invalid_status` (409) for any other step. - GET
/driver/earningsGiriş gerekirWhat the driver madeThe driver's completed rides over today, this week (from Monday) or this month, in `RidesConfig.timezone`, with each day's totals.
Taxi
Roadmap 105.9. A live taxi app (`Config.taxi`): the rides of the Rides and Driver tags, on a full-screen map where the free taxis around the rider and the rider's driver move as they move. The map is the company's choice: Google Maps with the company's own key, or OpenStreetMap from the company's tile server (`TaxiConfig.map`). Place search and routes go through Yallapp (`/maps/places`, `/maps/route`; 105.9's `/taxi/places` and `/taxi/route` are the same) when the company set them up. Roadmap 112.1. Every ride app has `Config.taxi` and these endpoints: ride hailing apps (vertical not `taxi`) too, on the company's map when it set one on Maps & live, else on the platform's map (tiles, and its search and routing servers when set). `tiles_url` is null when there's no map at all: the app keeps the same map-first screens on a plain background. Live positions are in the company's own Firebase Realtime Database (`TaxiConfig.live`). The app signs in to Firebase with the custom token of `POST /taxi/live-token`, whose claims say what it may read and write, under `root` (`yallapp/<app_id>`): - `cells/<cell>/<driver_key>`: `{lat, lng, heading, type, at}`, a free online driver, in the 5-letter geohash cell of their position. Any signed-in person of the app reads them; the driver writes their own (every 2 seconds while online and free; removed when they go offline, take a ride or move to another cell, and on disconnect). `type` is the driver's vehicle type ID, `at` milliseconds since 1970. Riders read the cell they are in and the eight around it and drop drivers whose `at` is over a minute old. - `drivers/<driver_key>`: `{lat, lng, heading, at}`, written by the driver every 2 seconds while online; read by the driver and by the rider whose token follows them (claim `follow`). - `rides/<ride_id>`: `{status, driver_key, at}`, written by Yallapp at each stage of the ride; read by its rider (claim `ride`) and its driver. Without a live database (`live` null), the app asks `GET /taxi/nearby` and `GET /me/rides/{ride_id}` every `nearby_every_seconds`. The driver's app also sends `PUT /driver/location` as in the Driver tag: rides are offered from those positions.
- POST
/taxi/live-tokenGiriş gerekirA token to sign in to the live databaseA Firebase custom token for the company's Firebase project (sign in with it, then use `database_url`), valid for an hour. Its claims: `app` (the app's ID), `role` (`rider` or `driver`), `driver_key` (a driver's own), `ride` and `follow` (the rider's ride that isn't over and its driver's key). Ask for a new one when it expires and when the ride changes (a driver accepted it, it ended). `feature_disabled` (403) when the app has no live database. - GET
/taxi/nearbyGiriş isteğe bağlıThe free taxis around a placeThe online drivers with no ride, whose position is under a minute old, within the company's matching distance of the place, nearest first, at most 30. For apps without a live database, and for the first look before the live one answers. Positions are rounded to about 10 metres. - GET
/taxi/placesGiriş isteğe bağlıPlaces for where toThe same as `GET /maps/places`, kept for the taxi apps built before roadmap 112. Places matching what the rider typed, near them first, from the company's Google Maps (Places) or OpenStreetMap search server (`TaxiConfig.map.search`). A Google place comes without its position: ask `GET /taxi/places/{place_id}` when the rider picks it. `feature_disabled` (403) when the company has no search. - GET
/taxi/places/{place_id}Giriş isteğe bağlıA place's position - POST
/taxi/routeGiriş isteğe bağlıThe route between two placesThe driving route, from the company's Google Maps (Routes) or OpenStreetMap routing server (`TaxiConfig.map.routes`), to draw on the map and to tell how long. `feature_disabled` (403) when the company has none: the app draws a straight line.
Maps
Roadmap 112. Place search and routes on the map an app draws, through Yallapp, for ride, taxi, delivery and food apps: the company's own map (`TaxiConfig.map`, set on Maps & live) or, for apps without one, the platform's (ride hailing apps' `TaxiConfig.map`, `DeliveryConfig.map`, `FoodConfig.map`). `search` and `routes` on those say whether `GET /maps/places` and `POST /maps/route` work; without them the app chooses places by the phone's position, a maps link or on the map, and draws a straight line. Roadmap 112.6. With the platform's MapTiler tiles (or a company's) and no search server, the search is MapTiler's geocoding with the tiles' key, from Yallapp's servers. `GET /maps/address` names the place at the phone's position, for "My location", wherever `search` is true.
- GET
/maps/placesGiriş isteğe bağlıPlaces by namePlaces matching what the person typed, near them first, on the app's map: the company's Google Maps (Places) or OpenStreetMap search server, or the platform's search server, or 112.6 MapTiler's geocoding with the tiles' key (`search` in `TaxiConfig.map`, `DeliveryConfig.map`, `FoodConfig.map`). A Google place comes without its position: ask `GET /maps/places/{place_id}` when the person picks it. `feature_disabled` (403) when the app's map has no search. - GET
/maps/addressGiriş isteğe bağlıThe address at a positionRoadmap 112.6. The place at the phone's position, named in the person's language, so "My location" fills the address: the company's Google Maps (Geocoding API), its or the platform's Nominatim server (`/reverse`), or MapTiler's geocoding with the tiles' key. Works wherever `search` is true; `feature_disabled` (403) otherwise, and `not_found` (404) when the map names nothing there. The app keeps its field as it is on any error. - GET
/maps/places/{place_id}Giriş isteğe bağlıA place's position - POST
/maps/routeGiriş isteğe bağlıThe route between two placesThe driving route on the app's map: the company's Google Maps (Routes) or OpenStreetMap routing server, or the platform's routing server (`routes` in `TaxiConfig.map`, `DeliveryConfig.map`, `FoodConfig.map`), to draw on the map and to tell how long. `feature_disabled` (403) when the map has no routes: the app draws a straight line.
Teachers
Roadmap 105.3. Academies with many teachers, on Yallapp's hosted API (`Config.instructors`). Each course may belong to one of the academy's teachers (`CourseSummary.instructor.id` is the teacher's ID; 0 for a course of the academy itself). Students browse the teachers, open a teacher's page with their courses and reviews, follow a teacher (a push when a new course of theirs is published) and rate a teacher once they are a student in one of their courses. When the academy lets people apply (`InstructorsConfig.applications`), a signed-in person can apply to teach and waits for the academy's answer. Teachers' shares are the academy's: Yallapp sets none.
- GET
/teachersGiriş isteğe bağlıThe academy's teachersActive teachers, the academy's featured ones first, then by rating. `feature_disabled` (403) when the app has no teachers (`Config.instructors` null). - GET
/teachers/{teacher_id}Giriş isteğe bağlıA teacher's page - PUT
/teachers/{teacher_id}/followGiriş gerekirFollow a teacherThe student gets a push (`new_course`) when a new course of the teacher is published. - DELETE
/teachers/{teacher_id}/followGiriş gerekirStop following a teacher - GET
/teachers/{teacher_id}/reviewsGiriş isteğe bağlıStudents' reviews of a teacherNewest first. A review the academy hid is sent to its author only. - PUT
/teachers/{teacher_id}/reviews/mineGiriş gerekirRate a teacherOne review per student and teacher, changed by sending it again. Only a student enrolled in one of the teacher's courses may rate them (`not_student`, 403); a teacher can't rate themselves. - GET
/teachers/applicationGiriş gerekirThe signed-in person's application to teach404 when they never applied. - POST
/teachers/applicationGiriş gerekirApply to teachOnly when `InstructorsConfig.applications` is true (`feature_disabled`, 403, otherwise). The academy sees the application on its Teachers page and approves or declines it; once approved, the person is a teacher. Sending it again replaces a declined application; a pending or approved one answers `already_applied` (409).
Teacher
Roadmap 105.3. The teacher's screens. A teacher is a person the academy added on the dashboard's Teachers page by mobile number (or by email, for apps that sign in by email only), or whose application it approved; whoever signs in with that number is that teacher (`User.roles` has `teacher`). Every endpoint here answers `not_teacher` (403) to anyone else, and to teachers the academy suspended. A teacher makes courses (title, description, price, level), adds sections and lessons (a video or a PDF sent from the phone, or a video address) and sends a course for review; the academy publishes it or sends it back with a note. Lessons added to a published course are live at once. A teacher answers their students' questions through `POST /questions/{question_id}/answers` (the answer's author has `instructor` true) and sees what their courses sold and their share.
- GET
/teacher/meGiriş gerekirThe signed-in teacher - PATCH
/teacher/meGiriş gerekirChange the teacher's bio and subjects - GET
/teacher/coursesGiriş gerekirThe teacher's coursesEvery course of the teacher, whatever its stage, newest first. - POST
/teacher/coursesGiriş gerekirStart a courseThe course starts as a draft, which only the teacher and the academy see. - GET
/teacher/courses/{course_id}Giriş gerekirOne of the teacher's courses, with its sections and lessons - PATCH
/teacher/courses/{course_id}Giriş gerekirChange a draft or returned courseOnly a `draft` or `returned` course (`not_editable`, 409, otherwise); the academy changes published ones. - POST
/teacher/courses/{course_id}/submitGiriş gerekirSend a course for reviewA `draft` or `returned` course with at least one lesson goes to the academy (`in_review`); `no_lessons` (409) without one, `not_editable` (409) at another stage. - POST
/teacher/courses/{course_id}/sectionsGiriş gerekirAdd a section to a courseNot while the course is `in_review` (`not_editable`, 409). - POST
/teacher/courses/{course_id}/lessonsGiriş gerekirAdd a lessonA lesson in one of the course's sections: a video (`upload_id` of a finished upload of a video, or `video_url`, a YouTube, Vimeo or https video address), a PDF (`upload_id` of a PDF, attached to the lesson) or text only. Uploads go to the academy's Media library and count in its storage. In a published course the lesson is live at once (and students who follow the course are told, like any new lesson). Not while the course is `in_review` (`not_editable`, 409). - DELETE
/teacher/courses/{course_id}/lessons/{lesson_id}Giriş gerekirRemove a lessonFrom a `draft` or `returned` course only (`not_editable`, 409); the academy removes lessons of published courses. - POST
/teacher/uploadsGiriş gerekirStart sending a lesson's video or PDFVideos (MP4, MOV, WebM) up to 5 GB and PDFs up to 200 MB, into the academy's Media library (413 `storage_full` when its storage is full), named with the teacher's name. The app sends the file in chunks to `upload_url` as for chat uploads, calls `/complete`, then adds the lesson with `upload_id`. A file not used in a lesson stays in the Media library, where the academy can delete it. - POST
/teacher/uploads/{upload_id}/resumeGiriş gerekirA fresh link to carry on with an upload - POST
/teacher/uploads/{upload_id}/completeGiriş gerekirFinish an upload409 `incomplete` with the offset reached when part of the file is missing. - GET
/teacher/questionsGiriş gerekirStudents' questions in the teacher's coursesWaiting for an answer first, then the newest. The teacher answers with `POST /questions/{question_id}/answers`, which marks the question answered. - GET
/teacher/submissionsGiriş gerekirAssignments handed in on the teacher's coursesRoadmap 112.5. `submitted` (the default) waits for a grade, the first handed in first; `returned`, `graded` and `all` the newest first. - PUT
/teacher/submissions/{submission_id}/gradeGiriş gerekirGrade an assignment, or send it back for another tryRoadmap 112.5. `grade` with a score from 0 to the assignment's highest (and a note), or `return` with a note saying what to change. The student is told in the app (`assignment_graded`). Roadmap 113.7 With a rubric, `rubric` gives the level chosen for each criterion and the score is their points added up. - GET
/teacher/earningsGiriş gerekirWhat the teacher's courses sold and the teacher's shareSales paid on the academy's payment page over this month, last month or this year, in `InstructorsConfig.timezone`, less refunds, with the teacher's share (`share_percent`, set by the academy) and each course's totals. The academy pays its teachers itself.
School
Roadmap 105.4. A school's app for its students and their parents, on Yallapp's hosted API. The school adds its students, parents and teachers on the dashboard (by mobile number, or by email for apps that sign in by email only), links each parent to their children, and puts the students in classes. Whoever signs in with a student's number is that student; with a parent's number, that parent (`User.roles` has `student` or `parent`). `GET /school/me` lists whom the person may see: a student themselves, a parent each of their children. Every `/school/students/{student_id}` endpoint answers `not_found` (404) for a student the person may not see, and `not_school_member` (403) to anyone the school didn't add. A student sees their timetable, attendance, homework (and hands it in), grades, upcoming exams and fees; a parent sees the same for each child and pays fees by card through the school's own payment page. Every amount is the school's: Yallapp sets none. 113.8 Everyone sees the school's calendar, the exams' timetable once published and each student's behaviour record. 105.4.7b The store review account (App sign-in's review number or email with its fixed code) is, the first time it signs in, a student of a class of samples, that student's parent and the class's teacher (and a member of staff where the school has staff).
- GET
/school/meGiriş gerekirWhom the signed-in person may see at the schoolA student sees themselves; a parent each of their children, in the order the school added them. A teacher who is also a parent sees their children here too. - GET
/school/messagesGiriş gerekirMessages with teachers, as the school allows113.6 For every school person (student, parent, teacher): what the school allows (whom teachers write to, who writes first, the hours, files, a class at once, whether the administration reads them) and whom they can write to: a teacher the parents (and students, when allowed) of the classes they teach; a parent the teachers of their children; a student their own teachers. A conversation is opened with `POST /school/messages/conversations` and then works as any Chat conversation (`Config.chat.base_url`). Everyone also has their conversation with the school's administration (`office_conversation_id`). - POST
/school/messages/conversationsGiriş gerekirOpen a conversation with a contact113.6 The conversation of the person and one of their contacts (`SchoolContact.person_id`), made the first time; anyone else is 404. Writing in it may still wait for the teacher to write first (`SchoolChatInfo.note`). - GET
/school/students/{student_id}/timetableGiriş gerekirThe student's class timetable for the week - GET
/school/students/{student_id}/attendanceGiriş gerekirThe student's attendance over a periodDays a teacher took attendance, newest first, with how many of each. The current term (`SchoolConfig.term`) by default, else the last 90 days. - GET
/school/students/{student_id}/homeworkGiriş gerekirThe homework of the student's class`due`: not handed in yet, soonest due first; `done`: handed in or past due, newest first. - PUT
/school/students/{student_id}/homework/{homework_id}/submissionGiriş gerekirHand in homeworkBy the student only (a parent gets `not_student`, 403): some text, a file (a photo or a PDF sent with `/school/uploads`), or both. It can be handed in again until the teacher marks it; after the due date it is marked late. Refused with `already_marked` (409) once marked. - GET
/school/students/{student_id}/gradesGiriş gerekirThe student's scores by subject, and the exams coming up - GET
/school/students/{student_id}/report-cardGiriş gerekirThe student's report cardRoadmap 112.5. Over the school's current term (else the year so far), or `from` to `to`: the student's percent in each subject (the share of the marks of the subject's scored exams, quizzes and assignments in the period) with each score, overall, and their attendance. - GET
/school/students/{student_id}/reportsGiriş gerekirThe teachers' reports the school shared with the student's parentsRoadmap 113.5. For the student's parents only (`not_parent`, 403, for anyone else, the student too): the reports teachers wrote on the student (their level, behaviour, attendance, what to do next) that the school approved and chose to share, newest first. The parents get a push (`school_report_shared`) when one is shared. - GET
/school/students/{student_id}/feesGiriş gerekirThe student's fees, due and paidFor the student's parents (`not_parent`, 403, for anyone else, the student too); at a school in university mode (113.9) the student sees and pays their own as well. `school_fee` pushes go to the same people. - POST
/school/students/{student_id}/fees/{fee_id}/payGiriş gerekirPay a fee by card on the school's payment pageWhen the school takes fees by card (`SchoolConfig.fees_by_card`) and has set up its payment gateway; `payments_unavailable` (503) otherwise, and `already_paid` (409) for a fee paid. The app opens `payment_url`; once paid the fee is `paid` with its receipt. For the same people as the fees (`not_parent`, 403, otherwise). - GET
/school/calendarGiriş gerekirThe school's calendar for the personRoadmap 113.8. The events, holidays and breaks (no school that day) and exam days from `from` to `to` (this month and the next weeks by default, up to 120 days), with the published exams of the timetable: everyone's, the grade's and the class's of the person's students (a teacher's: of their classes), and the staff's for those who work at the school. With `student_id`, one of the person's students only (a parent's child); `not_found` (404) for anyone else. - GET
/school/students/{student_id}/examsGiriş gerekirThe student's exams' timetableRoadmap 113.8. The published exams of the student's class or grade: those coming (soonest first) and the last 50 done (latest first). The student and their parents get a push when exams are published and the day before each (`school_exam`). - GET
/school/students/{student_id}/behaviourGiriş gerekirThe student's behaviour recordRoadmap 113.8. Merits and notes, newest first, with the record's points total and counts. Parents get a push (`school_behaviour`) for all of them, only negative ones or none, as the school chooses. - GET
/school/announcementsGiriş gerekirThe school's news for the personNewest first. To the whole school, to the class of one of the person's students, or to one of them (and their family). A teacher also sees what they wrote. - POST
/school/uploadsGiriş gerekirStart sending a file for homeworkA photo (JPEG, PNG, WebP) up to 20 MB or a PDF up to 200 MB, by a student handing in homework or a teacher giving it, into the school's Media library (413 `storage_full` when its storage is full). Sent in chunks to `upload_url` as chat files are, then `/complete`. - POST
/school/uploads/{upload_id}/resumeGiriş gerekirA fresh link to carry on with an upload - POST
/school/uploads/{upload_id}/completeGiriş gerekirFinish an upload409 `incomplete` with the offset reached when part of the file is missing. - GET
/school/students/{student_id}/busGiriş gerekirThe student's school bus, live while it runsRoadmap 113.10. Each trip (morning, afternoon) the student rides: the route and its bus, the stops in the order the bus reaches them and the student's stop; `run` is today's trip once the driver started it, with the bus's position while it runs (`bus`, recent ones only, drawn on `SchoolConfig.map`) and how soon it reaches the student's stop (`eta_minutes`, `distance_m`: before they get on in the morning, while they're on the bus in the afternoon), and how the driver or the supervisor marked the student. Ask again every 15 seconds or so while the screen is open. Parents are pushed (`school_bus`) when the bus is near the stop and when the child gets on and off. - GET
/school/bus/routesGiriş gerekirThe routes the person drives or supervises, with today's tripsRoadmap 113.10. For the school's people who drive or supervise one of its buses (`User.roles` has `bus`); `not_bus_crew` (403) for anyone else. `drives`: the person starts the route's trips and their phone sends the bus's position (the driver; without one, the supervisor). - POST
/school/bus/routes/{route_id}/runsGiriş gerekirStart the route's morning or afternoon trip todayRoadmap 113.10. By whoever `drives` the route. A trip started again the same day goes on where it was (200; 201 when it is new); `trip_running` (409) while the bus is on its other trip. While it runs, the driver's phone sends its position (`PUT /school/bus/runs/{run_id}/location`) as long as the trip's screen is open; nothing runs in the background. - GET
/school/bus/runs/{run_id}Giriş gerekirA trip as its crew sees it, the stops with their students - PUT
/school/bus/runs/{run_id}/locationGiriş gerekirThe bus's position, from the driver's phone while the trip runsRoadmap 113.10. At most every 5 seconds (more often is accepted and left out). The stops the bus comes within about 120 m of are reached; the parents of the stops it is now near (as the school chose: a distance or minutes) are pushed once each. `trip_ended` (409) once it ended. - PUT
/school/bus/runs/{run_id}/riders/{student_id}Giriş gerekirMark a student on the trip as on the bus, off it, or absentRoadmap 113.10. By the route's driver or supervisor, for a student of the trip. The parents are pushed (`school_bus`) when the child gets on and off. - POST
/school/bus/runs/{run_id}/endGiriş gerekirEnd the tripRoadmap 113.10. By whoever `drives` the route; the bus's position is kept only while a trip runs. A trip left running ends on its own the next day. - GET
/school/library/booksGiriş gerekirThe school library's books, searched and by categoryRoadmap 113.11. For everyone the school added. `q` matches the title, the author or the ISBN; `categories` are the library's, to browse by. - GET
/school/library/books/{book_id}Giriş gerekirA book, its copies on the shelf, its e-book and the person's holds of itRoadmap 113.11. `file`: the e-book (a PDF) to read in the app, by a link that lasts a few hours. `borrowers`: whom the person may reserve it for (themselves, a parent's children). - POST
/school/library/books/{book_id}/holdGiriş gerekirReserve the bookRoadmap 113.11. Held now when a copy is free (`ready`, until `expires_at`: the school's days), else `waiting` until one comes back, in the order people reserved; they're pushed (`school_library`) when it is ready. `person_id`: whom it is for, when the person borrows for more than one (a parent's children). `already_held`, `on_loan_to_you`, `too_many_holds` and `no_copies` (409). - DELETE
/school/library/holds/{hold_id}Giriş gerekirCancel a hold; its copy goes to the next one waiting - GET
/school/library/loansGiriş gerekirThe books out now, borrowed before and reservedRoadmap 113.11. Of whoever the person follows: themselves (a student, a teacher), a parent's children; one of them with `person_id`. Books are lent and returned at the library's desk; the student and their parents are pushed (`school_library`) before the due date and when a book is overdue.
Classroom
Roadmap 105.4. The school's teachers' screens. A teacher is a person the school added as a teacher on the dashboard; whoever signs in with their number is that teacher (`User.roles` has `teacher`). Every endpoint here answers `not_teacher` (403) to anyone else. A teacher sees the classes and subjects they teach and their timetable, takes attendance in a tap (the parents of a student marked absent or late are told), gives homework and marks what was handed in, enters the scores of exams and quizzes, and writes to a class (its students and their parents).
- GET
/classroom/meGiriş gerekirThe signed-in teacher, their classes and today's lessons - GET
/classroom/timetableGiriş gerekirThe teacher's lessons for the week - GET
/classroom/classes/{class_id}/studentsGiriş gerekirThe students of a class the teacher teaches - GET
/classroom/classes/{class_id}/gradebookGiriş gerekirA class's gradebookRoadmap 112.5. For the class's teachers, over the current term (else the year so far) or `from` to `to`: each student's percent in each subject taught, overall and attendance. - GET
/classroom/classes/{class_id}/syllabusGiriş gerekirThe class's progress through the syllabusRoadmap 113.5. The syllabus of each subject the teacher teaches in the class, for the class's grade (as the school wrote it: units, and their lessons or topics with the days the plan gives each), with the topics the class covered and where the plan says it should be today (a topic is due once its last day has passed; `behind` when fewer are covered than are due). - PUT
/classroom/classes/{class_id}/syllabus/{topic_id}Giriş gerekirMark a topic coveredRoadmap 113.5. On a day (today by default, up to a year back) with an optional note; marking it again changes the day and the note. Of a subject the teacher teaches in the class. - DELETE
/classroom/classes/{class_id}/syllabus/{topic_id}Giriş gerekirMark a topic not covered - GET
/classroom/classes/{class_id}/attendanceGiriş gerekirA class's attendance on a dayEvery student of the class, with their mark that day (null when not taken). Today in the school's time zone by default. - PUT
/classroom/classes/{class_id}/attendanceGiriş gerekirTake a class's attendanceFor today or a day in the last 7 (`date_out_of_range`, 400, else). Students left out keep their mark. When the school tells families (`SchoolConfig.absence_alerts`), the parents of a student newly marked absent or late get a push (`school_absence`). - GET
/classroom/homeworkGiriş gerekirThe homework the teacher gaveNewest first, of every class they teach or one (`class_id`). - POST
/classroom/homeworkGiriş gerekirGive homework to a classFor a subject the teacher teaches in that class (`not_your_class`, 403, else). The class's students and their parents get a push (`school_homework`). - GET
/classroom/homework/{homework_id}Giriş gerekirHomework with what each student handed in - PUT
/classroom/homework/{homework_id}/submissions/{student_id}Giriş gerekirMark a student's homeworkAlso when nothing was handed in. The student and their parents get a push (`school_grade`). - GET
/classroom/assessmentsGiriş gerekirThe exams, quizzes and assignments the teacher setComing up first (soonest first), then past ones (newest first), of every class they teach or one (`class_id`). - POST
/classroom/assessmentsGiriş gerekirSet an exam, a quiz or an assignment for a classFor a subject the teacher teaches in that class. It shows to the class's students and parents among the exams coming up. - GET
/classroom/assessments/{assessment_id}/scoresGiriş gerekirEvery student of the class with their score - PUT
/classroom/assessments/{assessment_id}/scoresGiriş gerekirEnter scoresFrom 0 to the assessment's `max_score`; null clears one. Students left out keep theirs. A student given a score for the first time, and their parents, get a push (`school_grade`). - POST
/classroom/classes/{class_id}/messagesGiriş gerekirWrite to a whole class at once113.6 When the school allows it (`SchoolMessages.class_messages`): the same text in each one-to-one conversation of the teacher with the class's parents, students or both (`families`, as the school allows), so each answer comes back to the teacher alone; each of them gets a push. People who never signed in to the app can't be written to yet (`not_in_app`). Within the school's hours (409 `outside_hours`). The same `client_id` again sends nothing twice. - GET
/classroom/examsGiriş gerekirThe exams of the teacher's classesRoadmap 113.8. The published exams of the teacher's classes (or of `class_id`), coming and done, as students see them. - GET
/classroom/behaviour/categoriesGiriş gerekirThe school's behaviour categoriesRoadmap 113.8. The categories the school uses, positive (points of 0 or more) and negative (0 or less), in the school's order. - POST
/classroom/behaviourGiriş gerekirWrite a merit or a noteRoadmap 113.8. On a student of one of the teacher's classes (`not_your_class`, 403, else), of one of the school's categories with its points. The student's parents get a push (`school_behaviour`) as the school chooses. - DELETE
/classroom/behaviour/{note_id}Giriş gerekirTake back a merit or a noteThe teacher's own, within a day of writing it (`too_late`, 409, after). - GET
/classroom/classes/{class_id}/behaviourGiriş gerekirA class's behaviourRoadmap 113.8. Each student's points and counts, and the class's latest 50 merits and notes (one student's with `student_id`). - POST
/classroom/announcementsGiriş gerekirWrite to a classTo a class the teacher teaches; its students and their parents see it with the school's news and get a push (`school_announcement`). - GET
/classroom/report-templatesGiriş gerekirThe school's report templatesRoadmap 113.5. The templates the school wrote for teachers' reports on a student or on a class, by kind and name. - GET
/classroom/reportsGiriş gerekirThe teacher's reportsRoadmap 113.5. The teacher's own reports to the school, the last changed first: drafts, sent, sent back for changes and approved; a student's (their history) or a class's. - POST
/classroom/reportsGiriş gerekirStart a report (a draft)Roadmap 113.5. On one of the school's templates: a student template needs `student_id` (a student of a class the teacher teaches), a class template `class_id` (a class they teach; the period is the school's term, else the year so far, when not given). Answers are checked against the template's fields (text up to 2000 characters, one of a choice's choices, a rating from 1 to 5); required ones are needed only to send it. - GET
/classroom/reports/{report_id}Giriş gerekirOne of the teacher's reports, with the school's comments - PUT
/classroom/reports/{report_id}Giriş gerekirChange a draft, or a report sent backRoadmap 113.5. A report with the school (sent or approved) can't be changed (`report_sent`, 409). - DELETE
/classroom/reports/{report_id}Giriş gerekirDelete a draft - POST
/classroom/reports/{report_id}/sendGiriş gerekirSend a report to the schoolRoadmap 113.5. A draft, or a report the school sent back, with every required field filled in (`incomplete_report`, 400, else) and an optional note; the school's administration hears of it on Yallapp Business. The teacher gets a push (`school_report`) when the school comments on it, sends it back for changes or approves it.
Check-in
Roadmap 113.2. Students, teachers and staff check in and out at the school from the app: with the gate's code (a QR code and its six digits on the school's Gate screen, new every 30 seconds and signed by the platform, so an old or made-up code fails; nothing secret is in the app), inside the school's area (the phone's `lat` and `lng` against the school's place and radius), either one, or both at once, as the school chooses (`methods`). Checking in after the school's `late_from` is late; checking out before `early_before` is leaving early. A teacher checks in as a teacher, a member of staff as staff, a student as a student (with the school's student times); a parent doesn't check in (`not_checkin_member`) and follows each child's days with `GET /school/students/{student_id}/checkins`, and is pushed (`school_checkin`) when the school wants. The school's office corrects a day in the dashboard with a reason, kept on the day (`corrections`). The teacher's class register (`/classroom/classes/{class_id}/attendance`) stays apart.
- GET
/school/students/{student_id}/checkinsGiriş gerekirA student's month at the gate, for them and their parents - GET
/school/checkinGiriş gerekirToday at the gate for the signed-in student, teacher or member of staffWhether the school takes check-ins, how, its place and radius (for the location method), the person's times and today's record. - POST
/school/checkinGiriş gerekirCheck in or out`action` `in` or `out`, with what the school's `methods` ask: the gate's `code` (the six digits typed, or what its QR code holds), the phone's `lat` and `lng`, or both. Errors: `checkin_off` (403, the school doesn't take check-ins), `outside_area` (403, with `details.distance_m`), `code_needed`, `invalid_code` (an old or another school's code) and `location_needed` (400), `not_school_day`, `too_early` (check-in opens two hours before the day starts), `already_in`, `not_in` and `already_out` (409). A student's parents get `school_checkin`. - GET
/school/checkin/historyGiriş gerekirThe signed-in person's month at the gate
Staff
Roadmap 113.3. Everyone who works at the school, teachers and staff (`User.roles` has `teacher` or `staff`); every endpoint here answers `not_staff` (403) to anyone else. Their job, department, working days and hours and duties as the school set them, their leave requests (sick, annual or other, with a note and a photo or PDF of a document sent with `/school/uploads`), approved or refused by the school in the dashboard with a note (`school_leave`), and the staff's own news, which students and parents never see (`school_staff`). Roadmap 113.12. Their own payslips once the school made them final (the base, allowances, bonuses, deductions, advances taken back, the net and the payments the school wrote down) with each one's PDF from a signed link that lasts 15 minutes, and their advances still being taken back. Salaries are recorded by hand: nothing here moves money, and no one sees another person's payslips.
- GET
/staff/meGiriş gerekirThe signed-in teacher or member of staff at workTheir job, department, working days and hours (else the school's times for teachers and staff), duties, and a teacher's lessons in the week. - GET
/staff/leaveGiriş gerekirThe person's leave requestsNewest first. - POST
/staff/leaveGiriş gerekirAsk for leaveFrom up to 30 days back to a year ahead, up to 90 days at a time; `leave_overlaps` (409) when a waiting or approved request has some of the days. A document is a photo or a PDF sent with `/school/uploads`. - DELETE
/staff/leave/{leave_id}Giriş gerekirWithdraw a leave request the school hasn't answered - GET
/staff/announcementsGiriş gerekirThe staff's own newsNewest first; for teachers and staff only. - GET
/staff/payslipsGiriş gerekirThe person's own payslipsRoadmap 113.12. Final payslips only (a draft the school is still writing is never shown), the newest month first; `advances` are the person's advances still being taken back. `pdf_url` is a signed link to the payslip's PDF in the app's language, good for 15 minutes: ask again for a new one. - GET
/staff/payslips/{payslip_id}Giriş gerekirOne of the person's payslips, with a new PDF link
Parent
Roadmap 113.4. The parent's area: an app's Parent tab, for the school's parents only (`User.roles` and `SchoolMe.roles` have `parent`); every endpoint here answers `not_parent` (403) to anyone else, a student, a teacher or a member of staff without children at the school, and `not_found` (404) for a child who isn't theirs. One account for all their children (`GET /parent/me`), each child's day at a glance (checked in and out, today's lessons, homework due, the latest grades, behaviour, the bus, library books, the reports the school shared, fees, slips to answer, an excuse, who picks the child up and the next days on the calendar), the permission slips the school sends a class, a grade or the whole school (signed yes or no with the parent's name by the due date; `school_slip` when one comes and the day before it is due), excuses for an absence (a day or several, a reason and a photo or PDF sent with `/school/uploads`; accepted or declined by the school, `school_excuse`; an accepted one makes those days `excused` in the attendance and at the gate), and who may pick the child up (name, relation, phone and a photo sent with `/school/uploads`) with a one-day note ("today X picks up") the school sees on the day's board. Fees, the reports, the calendar and messages stay on their School endpoints.
- GET
/parent/meGiriş gerekirThe signed-in parent and their childrenTheir children in the order the school added them (never the parent themselves), the slips each has to answer, and whether the school lets parents write to their children's teachers (113.6). - GET
/parent/children/{student_id}/dayGiriş gerekirA child's day at a glance - GET
/parent/slipsGiriş gerekirThe permission slips for the parent's childrenOpen ones first (the soonest due first), then those closed in the last 60 days; each with the parent's children it is for and their answers. `student_id` for one child's. - POST
/parent/slips/{slip_id}/answersGiriş gerekirSign a slip for a child, yes or noWith the parent's name. Answering again replaces the answer, until the end of the due date (`slip_closed`, 409, after it). - GET
/parent/children/{student_id}/excusesGiriş gerekirThe excuses sent for a childNewest first, up to 50. - POST
/parent/children/{student_id}/excusesGiriş gerekirExcuse a child's absenceFor a day or several, from 30 days back to 60 days ahead, up to 60 days at a time; `excuse_overlaps` (409) when a waiting or accepted excuse has some of the days. A photo or PDF is sent with `/school/uploads`. The school's administration is told on Yallapp Business. - DELETE
/parent/excuses/{excuse_id}Giriş gerekirWithdraw an excuse the school hasn't answered`already_answered` (409) once the school accepted or declined it. - GET
/parent/children/{student_id}/pickupGiriş gerekirWho may pick a child upThe list the parents keep (any of the child's parents changes it; the school adds to it in the dashboard), today's one-day note and the next days'. - PUT
/parent/children/{student_id}/pickupGiriş gerekirChange who may pick a child upThe whole list, up to 10 people; it replaces the one before. A photo is an image sent with `/school/uploads` (`photo_id` is its id); a photo already on the list stays when its `photo_id` is sent again. - PUT
/parent/children/{student_id}/pickup/noteGiriş gerekirSay who picks the child up on a day"Today X picks up": for today (when `date` is left out) or one of the next 30 days, one per day (sending again replaces it); the school sees it on that day's board. - DELETE
/parent/children/{student_id}/pickup/noteGiriş gerekirTake back a day's pick-up note
University
Roadmap 113.9. Schools in university mode (`SchoolConfig.university`): faculties, departments and programs; each term's sections of the courses with their lecturer, seats and weekly lectures. A student registers for the term in the app while its registration is open (seats left, lectures that don't clash, prerequisites passed, the program's credits per term; a full section takes a waiting list when the university has one), sends the registration to their advisor (a lecturer), who approves it or sends it back; a section is dropped until the term's drop date. Lecturers take each lecture's attendance, give the grades of the university's scale and publish a section's results; the student sees each term's GPA, the cumulative GPA (credit-weighted, the latest attempt of a course counting), the transcript (also as a PDF) and their program's study plan. A parent sees their child's part, read only. Endpoints answer `not_university` (404) when the school isn't a university.
- GET
/school/students/{student_id}/universityGiriş gerekirThe student's program, advisor, GPA and this term's registration - GET
/school/students/{student_id}/university/registrationGiriş gerekirThe student's registration for a term, and the sections offeredThe sections the student holds (asked for, enrolled or on a waiting list) and the term's other sections, each with its seats left and whether it can be added (`problems` says why not). - POST
/school/students/{student_id}/university/registration/sectionsGiriş gerekirAdd a section (the student only)While the term's registration is open and the registration isn't with the advisor. The section waits for the advisor (`pending`, its seat held), or joins the waiting list when it is full and the university has one (`waitlisted`). `not_allowed_clash`, `not_allowed_prerequisites`, `not_allowed_credits`, `not_allowed_full`, `not_allowed_taken`, `registration_closed`, `waiting_for_advisor` and `already_added` answer 409. - DELETE
/school/students/{student_id}/university/registration/sections/{section_id}Giriş gerekirDrop a section (the student only)Until the term's drop date (`drop_closed`, 409, after it). The first on its waiting list who fits takes the seat and is told (`uni_seat`). - POST
/school/students/{student_id}/university/registration/submitGiriş gerekirSend the registration to the advisor (the student only)With a section waiting for approval and at least the program's credits for a term (`credits_low`, 409). The advisor is told (`uni_advisee`). - GET
/school/students/{student_id}/university/timetableGiriş gerekirThe student's week of lectures in a term - GET
/school/students/{student_id}/university/resultsGiriş gerekirResults by term, each term's GPA and the cumulative GPAOnly the grades of sections whose results are published. - GET
/school/students/{student_id}/university/transcriptGiriş gerekirThe student's transcript, with its PDFThe results with the student and their program; `pdf_url` is the PDF's address, working for an hour. - GET
/school/students/{student_id}/university/planGiriş gerekirThe program's study plan, done, in progress and left - GET
/classroom/universityGiriş gerekirA lecturer's sections in a term, and their advisees waiting - GET
/classroom/university/sections/{section_id}Giriş gerekirOne of the lecturer's sections, its students and their grades - GET
/classroom/university/sections/{section_id}/attendanceGiriş gerekirA lecture's attendance (today by default) - PUT
/classroom/university/sections/{section_id}/attendanceGiriş gerekirSave a lecture's attendanceFor today and the 14 days before (`date_out_of_range`, 400). - PUT
/classroom/university/sections/{section_id}/gradesGiriş gerekirGive the section's gradesA letter of the university's scale, or a percent (`mark`) the scale's letter comes from; null clears it. For the section's enrolled students. - POST
/classroom/university/sections/{section_id}/publishGiriş gerekirPublish the section's resultsIts graded students (and their parents) see their grade and are told (`uni_results`). `nothing_graded` (409) without any grade. - GET
/classroom/university/adviseesGiriş gerekirThe students the lecturer advises, and their registration for a termThose waiting for an answer first. - GET
/classroom/university/advisees/{student_id}Giriş gerekirAn advisee's registration for a term - POST
/classroom/university/advisees/{student_id}/approveGiriş gerekirApprove an advisee's registrationIts sections waiting are enrolled; the student (and their parents) are told (`uni_registration`). `not_submitted` (409) when nothing waits. - POST
/classroom/university/advisees/{student_id}/returnGiriş gerekirSend an advisee's registration back, with a note
Bookings
Services, staff, free times and appointments, for Bookings apps. The plugin reads them from the site's booking plugin (Amelia, BookingPress or LatePoint); the booking plugin's own rules decide the free times and whether a new appointment is approved or waits for the business. Appointments are paid at the business.
- GET
/bookings/homeGiriş isteğe bağlıGet the bookings home screen sectionsSections in the order chosen in the plugin settings. Only sections with content are returned. - GET
/bookings/categoriesGiriş gerekmezList service categoriesCategories with services customers can book, in the booking plugin's order. - GET
/bookings/servicesGiriş gerekmezList servicesServices customers can book online, in the booking plugin's order. - GET
/bookings/services/{service_id}Giriş gerekmezGet a service and the staff who provide it - GET
/bookings/availabilityGiriş isteğe bağlıFree times for a serviceFree start times from `from` for `days` days, by the business's local date, as the booking plugin computes them (working hours, days off, breaks, existing appointments, buffers and how far ahead customers may book). Without `staff`, a time is free when any staff member who provides the service is free; `staff_ids` says who. - POST
/bookings/appointmentsGiriş gerekirBook an appointmentBooks a free time for the signed-in customer, through the booking plugin, so its emails, calendar sync and approval rules apply. The appointment is `approved` or `pending` as the plugin decides. Fails with `slot_taken` (409) when the time is no longer free and with `invalid_request` (400) for a service, staff member or time that can't be booked. When `bookings.intake_fields` lists questions (a clinic's intake form), `answers` holds the customer's answers by field ID. Fails with `invalid_answers` (400) when a required answer is missing or an answer doesn't fit its field; `details.fields` then maps field IDs to messages safe to show. Answers are health data: only the customer (in their appointments) and the business (in its dashboard) see them, and no push, email or text message includes them. - GET
/me/appointmentsGiriş gerekirList the customer's appointmentsUpcoming ones soonest first, past ones (and cancelled ones) newest first. - GET
/me/appointments/{appointment_id}Giriş gerekirGet one of the customer's appointments - POST
/me/appointments/{appointment_id}/cancelGiriş gerekirCancel one of the customer's appointmentsFails with `too_late` (409) when it starts sooner than `bookings.cancel_before_minutes` allows, or when the business doesn't let customers cancel.
Classes
Group classes and memberships, for gyms, studios and coaches (Bookings apps; the hosted API only for now). Classes come from the business's weekly timetable; each has a capacity, and a spot is booked only while one is left. When `bookings.classes.membership_required` is true, booking needs an active membership, and a plan with credits uses one per class (given back when the customer cancels in time). Memberships are paid on a web page outside the app, on Android and iOS alike: they are for a service used in person (App Store Review Guideline 3.1.3(e)).
- GET
/bookings/classesGiriş isteğe bağlıList classes on the timetableClasses from `from` to `to` (both included, the business's local dates), soonest first, also full and cancelled ones. Signed-in customers get `booked` for their own spots. Answers `not_found` (404) when the app has no classes. - POST
/bookings/classes/{class_id}/bookGiriş gerekirBook a spot in a classBooks one spot for the signed-in customer, only while one is left (no overbooking, also when many book at once). Booking a class already booked answers it again. Fails with `class_full` (409), `too_late` (409) once it has started or is closer than `min_minutes_ahead`, `class_cancelled` (409), `membership_required` (403) without an active membership when the business asks for one, `membership_frozen` (403, roadmap 64) when the active memberships are frozen on the class's day, and `no_credits` (403) when the membership's credits are used up. With a credit plan, one credit is used. - POST
/bookings/classes/{class_id}/cancelGiriş gerekirCancel the customer's spot in a classGives the spot back. A credit used for it comes back too. Fails with `too_late` (409) when the class starts sooner than `bookings.classes.cancel_before_minutes` allows, or when customers can't cancel in the app, and with `not_found` (404) when the customer has no spot in it. - GET
/memberships/plansGiriş isteğe bağlıList the membership plans on salePlans in the business's order. Empty when it sells none in the app. - POST
/memberships/checkout-linkGiriş gerekirGet a payment page link for a membershipOpens the business's payment page for one plan; paying starts the membership at once. The page's last address has `order-received` in it. Offered on Android and iOS: a membership is for a service used in person, not digital content. Fails with `feature_disabled` (403) when the business can't take card payments, and with `not_found` (404) for a plan not on sale. - GET
/me/membershipsGiriş gerekirList the customer's membershipsActive ones first (the one that ends first), then ended ones, newest first.
Platform
Calls from the Yallapp platform to a client's WordPress site (roadmap 31), never from the app. Only the Yallapp plugin serves them; the hosted API and a client's own API don't.
- POST
/chat/notifyGiriş gerekmezPush a new chat message to the site's app usersRoadmap 77. Sent by the Yallapp platform to a WordPress site when a message arrives for its app's people: the site keeps their push tokens, so its plugin sends the push (Firebase or Huawei Push Kit), with the notification preferences the person chose. Nothing about the message is stored on the site. Signed like `POST /license/refresh`: `signature` is the lowercase hex HMAC-SHA256 of `yallapp-chat-notify:<timestamp>:<sha256 of the notification JSON, hex>` keyed with the license key in capitals; a wrong signature, a site without a license key or a timestamp more than 5 minutes away is 401 `invalid_signature`. - POST
/campaigns/sendGiriş gerekmezSend a campaign from the Yallapp dashboard to the app's usersRoadmap 85.4. Sent by the Yallapp platform to a WordPress site when the owner sends a campaign from Dashboard › App notifications › Campaigns (now, at the time chosen, or each week): the site keeps its people's push tokens, so the plugin adds the notification to everyone's inbox (with its picture and what it opens) and pushes it to the phones that allow announcements, in batches. When the last batch is sent, the plugin reports how many phones it reached and missed with `POST /campaigns/report` (docs/api/platform.yaml). Signed like `POST /chat/notify`: `signature` is the lowercase hex HMAC-SHA256 of `yallapp-campaign:<timestamp>:<sha256 of the campaign JSON, hex>` keyed with the license key in capitals; a wrong signature, a site without a license key or a timestamp more than 5 minutes away is 401 `invalid_signature`. The same `send_id` twice is sent once (200 with the first answer). - POST
/billing/accessGiriş gerekmezGive or take access bought through a storeRoadmap 100. Sent by the Yallapp platform to the WordPress site (plugin 0.4.23 and later) once it has checked a store purchase, and again when Google or Apple says it was refunded, revoked, renewed or ended. The plugin gives the access (`grant`): enrols the user in the course (the site's LMS), or gives the membership until `expires_at` (Paid Memberships Pro level, LearnDash group, or the site's WooCommerce Subscriptions product), and records a WooCommerce order marked paid by the store when WooCommerce is active; or takes it back (`revoke`). `grant_id` is the same on every try of one change: a repeat answers as the first time did. The request is signed like `POST /license/refresh`: the headers `Yallapp-Timestamp` (Unix seconds) and `Yallapp-Signature`, the lowercase hex HMAC-SHA256 of `yallapp-billing-access:<timestamp>:<the raw body>`, keyed with the license key in capitals. A wrong signature, a site without a license key or a timestamp more than 5 minutes away is refused (`invalid_signature`, 401). Only sent to the https address of the site the license is active on; tried again after 1, 5 and 30 minutes and 2, 6 and 24 hours when the site doesn't answer 2xx. - POST
/license/refreshGiriş gerekmezCheck the license nowSent by the Yallapp platform, a few seconds after the app's owner saves something the plugin reads from its license status (the app type, how people sign in, the plan) or publishes the app's pages. The plugin then checks its license with the platform and fetches the published pages, as it does on its own twice a day and every 15 minutes. It trusts nothing in this request: it only learns that it should ask now. The request is signed with the site's license key, which both sides know: `signature` is the lowercase hex HMAC-SHA256 of `yallapp-license-refresh:<timestamp>` keyed with the license key in capitals (`YALL-XXXX-XXXX-XXXX`). The plugin refuses a wrong signature, a site without a license key and a timestamp more than 5 minutes away from its clock (`invalid_signature`, 401). It checks at most once every 30 seconds: a nudge within 30 seconds of the last one gets 429 and the plugin checks once when the 30 seconds are over, so the last change still arrives. Reachable during maintenance and while the app API is paused, so a renewed or reactivated plan reaches the site.
Links
Links that open the app (roadmap 46): Android App Links and iOS Universal Links. A shared course, product, service, listing, article, event, campaign or page is a link `<base>/<kind>/<id>`. It opens that screen in the app when the app is installed, and the website otherwise. - On Yallapp's link domain, for every app: `<base>` is `https://yallapp.com.tr/a/<app_id>`. Without the app, that address shows a page in the app's branding with its store links; for a WordPress app it sends the visitor on to the site's own link. - On a WordPress site with the Yallapp plugin: `<base>` is `<site>/a` (`Config.links.share_base_url`). Without the app, the plugin sends the visitor to the item's own page on the site (its permalink), or to the home page when the item has none or isn't public. | Kind | Link | Opens in the app | `id` | |---|---|---|---| | `course` | `<base>/course/12` | the course (Academy) | a number | | `product` | `<base>/product/12` | the product (Store) | a number | | `service` | `<base>/service/12` | the service (Bookings) | a number | | `listing` | `<base>/listing/12` | the listing (Real estate) | a number | | `article` | `<base>/article/431` | the article (News & blog) | letters, digits, `-` and `_` | | `event` | `<base>/event/12` | the event (Events & tickets) | a number | | `campaign` | `<base>/campaign/12` | the campaign (Charity & donations) | a number | | `page` | `<base>/page/offers` | a page built in the dashboard | its slug | The same table, with examples, is `docs/api/app-links.json`; the app, the platform and the plugin follow it. A link of a kind the app type doesn't have, or that doesn't match, opens the app's home. The phone checks that the domain trusts the app with two files the domain serves at its root, as JSON (`application/json`), without a redirect: `/.well-known/assetlinks.json` (Android: the package name and the SHA-256 fingerprints of the Play App Signing key and the upload key) and `/.well-known/apple-app-site-association` (iOS: `<Team ID>.<bundle ID>` with the paths `/a/*`, or `/a/<app_id>/*` on Yallapp's domain). The plugin builds them from its license status (`LicenseStatus.app_links` in `docs/api/platform.yaml`). Push notifications and notifications in the inbox may carry a `link` in the same format. **The site's own pages** (roadmap 48). On a WordPress site, the plugin also lists in `apple-app-site-association` the paths of the site's own pages for what the app shows: courses and lessons, products and product categories, posts, and the pages with a booking plugin's booking form, as the site's permalinks make them. It leaves out, before them, what must stay on the website: `wp-admin`, `wp-login.php`, the REST API, feeds, `xmlrpc.php`, `wp-content` and `wp-includes`, the cart, checkout, account and payment pages of WooCommerce and the course plugins, and addresses with `add-to-cart`, `wc-api`, `rest_route` or `preview` in their query. Android's App Links have no exclusions, and the app's intent filters are fixed when it's built: the plugin reports the permalink bases that start with a fixed folder (`/courses/`, `/product/`, `/product-category/`, `/blog/`) with its license check (`SiteInfo.link_paths`), and the next build lists them. Posts whose permalinks start at the root of the site (`/%postname%/`) open the app on iPhone only. The app asks `GET /links/resolve` what a page of the site is. When the answer is an item it shows, it opens that screen; otherwise (`kind` null, an error, an older plugin) it opens the address in its own in-app browser, which never hands the address back to the app, so a link never loops. A site in a folder (`example.com/learn`) can't serve the two files at its domain's root: the plugin serves them at `<site>/.well-known/…` too, for the domain's own server to copy, and until it does only Yallapp's links open the app.
- GET
/links/resolveGiriş gerekmezWhat a page of the site is in the appRoadmap 48. The app asks what an address on the site that opened it is: a course, a lesson, a product, a product category, a post, or a page with a booking form. `kind` is null when the address is something else (the home page, a page, the cart, an account page, another site): the app then opens it in its in-app browser. Only public, published items are named. Served by the Yallapp plugin; the hosted API answers 404 (its links are always `<base>/<kind>/<id>`), and so do plugins released before this endpoint: the app then opens the in-app browser too.
Crash reports
Crash reports (roadmap 46). Served by Yallapp for every app, at `https://yallapp.com.tr/api/app/{app_id}`, also when the app's content comes from WordPress or an own API: the app sends its crashes to Yallapp, never to the site. Off when `Config.app.crash_reports` is false.
- POST
/crashesGiriş gerekmezSend crash reportsThe app sends what went wrong (an uncaught error in the app's code), at most 10 reports per call, each one at most once per app run, with the sessions it counted since it last sent. Reports that couldn't be sent are kept on the phone (at most 10) and sent on the next start. A report carries no personal data: no names, phone numbers, emails, tokens, codes or addresses with query strings. The app removes them before sending, and Yallapp removes what still looks like one before it stores the report. Yallapp groups reports by `fingerprint` (the error's type and its first frames in the app's own code), counting them, the first and last time seen and the app versions. Rate limited per app and per address (`rate_limited`, 429 with `retry_after_seconds`). Answers 204 also when crash reports are off for the app, so the app doesn't keep them.