← Etusivulle

Mobiili-API

Koko mobiili-API kulkee oman domainin kautta osoitteessa api.meidankesken.app. Reitit ovat JSON-pohjaisia ja tukevat CORS:ia – erillistä API-avainta ei tarvita.

Yhteys
BASE_URL = https://api.meidankesken.app/api/public

const res = await fetch(BASE_URL + "/auth/login", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ email, password }),
});
const { access_token } = await res.json();

await fetch(BASE_URL + "/catalog");                       // julkinen
await fetch(BASE_URL + "/game/session", {                 // vaatii tokenin
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + access_token,
  },
  body: JSON.stringify({ player_names: ["Anna"], deck_slug: "syvat-vedet" }),
});

Kirjautumista vaativissa reiteissä lähetetään otsake Authorization: Bearer <access_token>.

Autentikointi

Rekisteröinti, kirjautuminen ja tokenin uusiminen saman domainin kautta.

POST /auth/signup
Julkinen
Rekisteröi uusi pelaaja. Etunimi ja sukunimi ovat pakollisia.

Kutsu

{ "email": "pelaaja@example.com", "password": "salasana123", "first_name": "Anna", "last_name": "Virtanen" }

Vastaus

{ "id": "uuid", "email": "pelaaja@example.com", "…": "…" }
POST /auth/login
Julkinen
Kirjautuminen. Palauttaa access- ja refresh-tokenin.

Kutsu

{ "email": "pelaaja@example.com", "password": "salasana123" }

Vastaus

{ "access_token": "…", "refresh_token": "…", "expires_in": 3600, "user": { "id": "uuid" } }
POST /auth/refresh
Julkinen
Uusi access token vanhentuneen tilalle.

Kutsu

{ "refresh_token": "…" }

Vastaus

{ "access_token": "…", "refresh_token": "…" }
POST /auth/logout
Token vaaditaan
Kirjaa käyttäjän ulos ja mitätöi istunnon.

Kutsu

{}

Vastaus

204 No Content
GET /profile
Token vaaditaan
Kirjautuneen pelaajan profiili nimitietoineen.

Vastaus

{ "id": "uuid", "first_name": "Anna", "last_name": "Virtanen", "display_name": "Anna Virtanen", "avatar_url": null }
PATCH /profile
Token vaaditaan
Päivittää etunimen, sukunimen tai avatarin. Näyttönimi päivittyy nimien mukana.

Kutsu

{ "first_name": "Anna", "last_name": "Virtanen" }

Vastaus

{ "id": "uuid", "first_name": "Anna", "last_name": "Virtanen", "display_name": "Anna Virtanen" }

Sisältö

Julkiset reitit – eivät vaadi kirjautumista.

GET /catalog
Julkinen
Kaikki julkaistut kategoriat ja niiden korttipakat.

Vastaus

{
  "categories": [
    {
      "id": "uuid", "slug": "parisuhde", "name": "Parisuhde",
      "description": "…", "icon": "heart", "color": "#b4674d", "sort_order": 1,
      "decks": [
        { "id": "uuid", "slug": "syvat-vedet", "name": "Syvät vedet",
          "theme": "läheisyys", "difficulty": 3, "default_timer_seconds": 60 }
      ]
    }
  ]
}
GET /deck/{slug}
Julkinen
Yksittäinen korttipakka ja sen kortit.

Kutsu

GET https://api.meidankesken.app/api/public/deck/syvat-vedet

Vastaus

{
  "deck": { "id": "uuid", "slug": "syvat-vedet", "name": "Syvät vedet", "default_timer_seconds": 60 },
  "cards": [
    { "id": "uuid", "content": "Mistä hetkestä muistat…",
      "type": "question", "hint": null, "sort_order": 1 }
  ]
}

Peli

Vaativat kirjautuneen käyttäjän bearer-tokenin. Data rajautuu omiin peleihin.

POST /game/session
Token vaaditaan
Luo pelisession, sekoittaa kortit ja arpoo vuorojärjestyksen (ei kahta vuoroa peräkkäin samalle pelaajalle).

Kutsu

{
  "player_names": ["Anna", "Mikko", "Sara"],
  "deck_slug": "syvat-vedet",   // tai deck_id
  "timer_seconds": 45,          // null = pakan oletus
  "shuffle": true
}

Vastaus

{
  "session": { "id": "uuid", "started_at": "…", "timer_seconds": 45,
               "player_names": ["Anna", "Mikko", "Sara"] },
  "deck": { "id": "uuid", "name": "Syvät vedet" },
  "cards": [ { "id": "uuid", "content": "…", "type": "question",
               "sort_order": 3, "turn_player": "Mikko" } ],
  "turn_order": ["Mikko", "Anna", "Sara", "…"]
}
POST /game/play
Token vaaditaan
Kirjaa kortin vastaukset. Lähetä kaikki kortin vastaajat kerralla player_names-listassa (skipped_players = ne jotka ohittivat). Vanha player_name-muoto toimii yhä.

Kutsu

{ "session_id": "uuid", "card_id": "uuid",
  "player_names": ["Anna", "Mikko", "Sara"],
  "skipped_players": ["Sara"] }

Vastaus

{ "ok": true, "played_count": 7,
  "cards_played": 7, "responses_count": 21 }
POST /game/complete
Token vaaditaan
Päättää session. Yhteenvedon played/answered/skipped lasketaan korttitasolla (ei kerraannu pelaajamäärällä); responses* kertoo yksittäiset vastaukset.

Kutsu

{ "session_id": "uuid" }

Vastaus

{
  "session": { "id": "uuid", "completed_at": "…" },
  "summary": { "played": 12, "answered": 11, "skipped": 1,
    "responses": 36, "responses_answered": 33, "responses_skipped": 3 }
}
GET /game/session
Token vaaditaan
Pelihistoria: 50 viimeisintä omaa sessiota.

Vastaus

[ { "id": "uuid", "player_names": ["Anna", "Mikko"],
    "started_at": "…", "completed_at": "…",
    "decks": { "name": "Syvät vedet", "slug": "syvat-vedet" } } ]

AI-pelinjärjestäjä

Keskustelu, puheentunnistus ja kertojaääni samalta domainilta. Kaikki vaativat tokenin.

POST /chat
Token vaaditaan
Striimaava keskustelu. mode: "host" = pakan valinta, "narrator" = kortin luku ja vuoron osoitus.

Kutsu

fetch("https://api.meidankesken.app/api/public/chat", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + accessToken,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ message: "Meitä on kolme, jotain kevyttä.", mode: "host" })
})

Vastaus

data: {"delta":"Kuulostaa"}
 data: {"delta":" hyvältä"}
 data: [DONE]
POST /speech
Token vaaditaan
Muuttaa kertojan tekstin MP3-puheeksi. Kutsu reittiä, kun AI:n vastaus tai kortti halutaan lukea ääneen.

Kutsu

const res = await fetch("https://api.meidankesken.app/api/public/speech", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + accessToken,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ text: "On Annan vuoro.", format: "base64" })
});
const { audio_base64, mime_type } = await res.json();

Vastaus

{ "audio_base64": "…", "mime_type": "audio/mpeg", "voice": "alloy" }
POST /transcribe
Token vaaditaan
Muuttaa mobiilisovelluksen äänitallenteen suomenkieliseksi tekstiksi. Lisää stream=true, niin teksti tulee heti osissa (text/event-stream). Nopein tulos: 16 kHz mono, lyhyet pätkät.

Kutsu

const form = new FormData();
form.append("file", audioBlob, "answer.wav");
form.append("language", "fi");
form.append("stream", "true"); // valinnainen, nopeuttaa näkyvää vastausta
const res = await fetch("https://api.meidankesken.app/api/public/transcribe", {
  method: "POST",
  headers: { "Authorization": "Bearer " + accessToken },
  body: form
});
// stream=true → data: {"type":"transcript.text.delta","delta":"…"}
// ilman streamia → const { text } = await res.json();

Vastaus

{ "text": "Tämä on minun vastaukseni.", "language": "fi" }

Virhekoodit

400Virheellinen pyyntörunko
401Puuttuva tai vanhentunut token
403AI-pelinjärjestäjä pois käytöstä
22023Virheellinen syöte (pelaajamäärä tai pakan tunniste)
P0002Resurssia ei löydy (pakka tai sessio)
P0003Pakassa ei ole julkaistuja kortteja
P0004Sessio on jo päättynyt