# Aihio: Dokumentaation täysteksti (LLM) > Aihio on suomalainen tekoälyagenttialusta. Tämä tiedosto sisältää > koko suomenkielisen dokumentaation tekstimuodossa LLM-pohjaisia > hakukokemuksia varten (ChatGPT, Claude, Perplexity). Lähde: https://aihio.ai/ohjekeskus Kuratoiva linkkihakemisto: https://aihio.ai/llms.txt # Tilin asetukset URL: https://aihio.ai/ohjekeskus/tili/asetukset Kuvaus: Tilin asetusten hallinta. Tiliasetuksia hallitaan ohjauspaneelin oikean yläkulman valikosta. ## Mitä voit muuttaa - Sähköpostiosoite ja näyttönimi - Salasana ja kaksivaiheinen tunnistautuminen - Ilmoitusasetukset — sovellusilmoitukset ja käytettävissä olevat sähköpostitapahtumat - Kielivalinta — hallintapaneelin ja sähköpostiviestien kieli > **Kaksivaiheinen tunnistautuminen:** > > Suosittelemme vahvasti käyttämään kaksivaiheista tunnistautumista. Se suojaa > tiliäsi myös siinä tapauksessa, että salasana paljastuu. --- # Tilauksen hallinta URL: https://aihio.ai/ohjekeskus/laskutus/tilauksen-hallinta Kuvaus: Laskutuksen ja tilausten hallinta. Tilaustiedot löytyvät ohjauspaneelin "Laskutus"-välilehdeltä. ## Mitä voit tehdä - Vaihtaa hinnoittelumallia — ilmainen, kasvu tai yritys - Päivittää maksutiedot — kortti tai laskutustiedot - Ladata laskut — PDF-muodossa kirjanpitoa varten - Peruuttaa tilaus — milloin tahansa, ilman selityksiä > **Maksutapa:** > > Hyväksymme luotto- ja debit-kortit (Visa, Mastercard) sekä yritysasiakkaille > laskutuksen. --- # Chatbotin asetukset URL: https://aihio.ai/ohjekeskus/tekoalyagentti/asetukset Kuvaus: Chatbotin asetusten hallinta. Chatbotin käyttäytymistä, ulkoasua ja tietopohjaa hallitaan asetus-välilehdiltä. ## Välilehdet - **Yleiset** — nimi, kieli, avoinna-ajat - **Ulkoasu** — pääväri, avausnapin väri, logo ja chat-widgetin sijainti - **Tietopohja** — dokumentit ja URL-osoitteet, joista chatbot oppii - **Vastaukset** — persoona, sävy, kielletyt aiheet - **Integraatiot** — sähköposti, Slack, Teams, CRM > **Muista tallentaa:** > > Muutokset tulevat voimaan vasta, kun painat **Tallenna**-painiketta. Pääväri vaikuttaa otsikkoon, omiin viesteihin ja ensisijaisiin painikkeisiin. Avausnapille voi valita oman värin, ja sen ikonin kontrasti valitaan automaattisesti. Näin esimerkiksi valkoinen otsikko ei pakota avausnappia valkoiseksi. ## Nykyisen sivun tunnistus **Tekoäly**-asetusten **Ymmärrä kävijän nykyinen sivu** -kytkin auttaa chatbotia tulkitsemaan viittaavia kysymyksiä. Esimerkiksi hinnoittelusivulla esitetty ”Mitä tähän sisältyy?” voidaan yhdistää kyseiseen tietopohjasivuun. Ominaisuus on oletuksena pois käytöstä. Sen voi ottaa käyttöön, kun chatbotin tietopohjassa on vähintään yksi valmis verkkosivu. Upotuskoodia tai sivuston asetuksia ei tarvitse muuttaa. Chat-widget tarkistaa sivun jokaisen lähetetyn viestin yhteydessä, joten myös yksisivuisen verkkosovelluksen osoitteenmuutokset huomioidaan automaattisesti. Tunnistus toimii vain, kun selaimen sivu vastaa yksiselitteisesti tietopohjaan lisättyä verkkosivua. Jos sivua ei löydy tai sama tunniste vastaa useaa lähdettä, chatbot käyttää tavallista tietopohjahakua. Sivukohtainen haku ei estä tavallisen haun parasta osumaa. ### Tietosuoja ja rajaukset - Chat-widget poistaa osoitteesta kyselyparametrit ja ankkurin sekä muodostaa alkuperästä ja polusta kiinteän tunnisteen selaimessa. - Chat-widget ei lähetä Aihiolle raakaa sivuosoitetta, sivun otsikkoa, tekstiä, otsikoita, lomakkeita, syötekenttiä tai muita DOM-tietoja. - Tunnistetta ei tallenneta keskusteluun eikä kirjoiteta sovelluslokeihin. Sitä käytetään vain kyseisen viestin tietopohjahaun rajaamiseen. - Ominaisuus kannattaa jättää pois käytöstä sivustoissa, joiden osoitepolku yksilöi käyttäjän tai sisältää arkaluonteista tietoa. Jos ominaisuus otetaan käyttöön tällaisessa ympäristössä, käsittely pitää arvioida ja kuvata sivuston omassa tietosuojaselosteessa. Ominaisuus on vapaaehtoinen eikä vaikuta chatbotin tavalliseen toimintaan, kun se on pois käytöstä. Tunnistus kannattaa testata sivustolle julkaistulla chat-widgetillä. Ohjauspaneelin esikatselu ei vastaa asiakkaan verkkosivun osoitetta, joten se käyttää tavallista tietopohjahakua. --- # Identiteettivarmistus URL: https://aihio.ai/ohjekeskus/tekoalyagentti/identiteettivarmistus Kuvaus: Sido kirjautunut käyttäjä kryptografisesti chat-widgettiin. HMAC-käyttäjätiiviste tai JWT – palvelin allekirjoittaa, Aihio vahvistaa. Ilman identiteettivarmistusta kuka tahansa kävijä voi kutsua `AihioWidget('identify', { user_id: '…' })` selaimen kehittäjätyökalusta millä käyttäjätunnuksella tahansa. Varmistus sitoo istunnon palvelimesi allekirjoittamaan salaisuuteen: Aihio tietää, että kutsuja on oikeasti kirjautunut käyttäjä. Aihio tukee kahta menetelmää: **HMAC-käyttäjätiivistettä** ja **JWT HS256 -tokenia**. ## Mistä salaisuus löytyy Avaa ohjauspaneelissa chatbotin **Asetukset → Suojaus**. Paina **Identiteetin varmistus** -osiossa **Luo salaisuus** ja kopioi arvo heti talteen: se näytetään vain kerran. Salaisuus näyttää esimerkiksi tältä: `aihio_idv_…`. Tallenna salaisuus palvelimesi ympäristömuuttujaksi (esim. `AIHIO_IDENTITY_SECRET`). Älä koskaan sisällytä sitä selainpuolen JavaScript-koodiin tai versiohallintaan. ## Menetelmä A: HMAC-käyttäjätiiviste Laske `HMAC-SHA256(salaisuus, user_id)` palvelimellasi ja välitä tulos `user_hash`-kentässä. Tulosteen **on oltava pienet kirjaimet sisältävä heksadesimaali**. Aihio hylkää isolla kirjoitetun heksan. Kaikki alla olevat esimerkit tuottavat oikean muodon oletuksena. ### Node.js ```javascript const crypto = require('crypto'); function computeUserHash(secret, userId) { return crypto.createHmac('sha256', secret).update(userId).digest('hex'); } ``` ### Python ```python import hmac, hashlib def compute_user_hash(secret: str, user_id: str) -> str: return hmac.new( secret.encode('utf-8'), user_id.encode('utf-8'), hashlib.sha256 ).hexdigest() ``` ### PHP ```php function compute_user_hash(string $secret, string $user_id): string { return hash_hmac('sha256', $user_id, $secret); } ``` ### Ruby ```ruby require 'openssl' def compute_user_hash(secret, user_id) OpenSSL::HMAC.hexdigest('SHA256', secret, user_id) end ``` ## Menetelmä B: JWT HS256 -token Allekirjoita JWT `HS256`-algoritmilla chatbotin salaisuudella. `exp`-väite on **pakollinen**: Aihio hylkää tokenin ilman sitä. Kellopoikkeama on 30 sekuntia. Suositeltu tokenin elinikä on 1 tunti (`exp = iat + 3600`). Älä myönnä yli 24 tunnin tokeneita. ### Tuetut JWT-väitteet | Väite | Tyyppi | Pakollinen | Huomio | | ------------------- | ------------------ | ------------------ | -------------------------------------- | | `user_id` tai `sub` | merkkijono | kyllä (jompikumpi) | Käyttäjätunnus omassa järjestelmässäsi | | `external_id` | merkkijono | ei | `user_id`/`sub`-alias | | `exp` | numero (Unix-aika) | kyllä | Vanheneminen; 30 s poikkeama | | `nbf` | numero (Unix-aika) | ei | Voimassa aikaisintaan; 30 s poikkeama | | `email` | merkkijono | ei | Esitäyttää esikeskustelulomakkeen | | `name` | merkkijono | ei | Esitäyttää esikeskustelulomakkeen | | `phonenumber` | merkkijono | ei | Esitäyttää esikeskustelulomakkeen | | `custom_attributes` | objekti | ei | Vapaamuotoiset lisätiedot | | `stripe_accounts` | taulukko | ei | Käyttäjän Stripe-tilit (ks. alla) | Vain `HS256` hyväksytään. RS256-, ES256- tai `alg: none` -tokeneja ei hyväksytä. ## Allekirjoita JWT palvelimella Allekirjoita token chatbotin salaisuudella `HS256`-algoritmilla. Aseta `exp` (suositus 1 tunti). `custom_attributes` on vapaaehtoinen. ### Node.js (`jsonwebtoken`) Asennus: `npm install jsonwebtoken` ```javascript const jwt = require('jsonwebtoken'); function signIdentityToken(secret, user) { return jwt.sign( { user_id: user.id, email: user.email, name: user.name, custom_attributes: { plan: user.plan }, }, secret, { algorithm: 'HS256', expiresIn: '1h' }, ); } ``` ### Ruby on Rails (`jwt`-kirjasto) Asennus: `bundle add jwt` ```ruby require 'jwt' def sign_identity_token(secret, user) payload = { user_id: user.id, email: user.email, name: user.name, custom_attributes: { plan: user.plan }, exp: Time.now.to_i + 3600, } JWT.encode(payload, secret, 'HS256') end ``` ### Django / Python (`PyJWT`) Asennus: `pip install PyJWT` ```python import time import jwt def sign_identity_token(secret: str, user) -> str: payload = { 'user_id': user.id, 'email': user.email, 'name': user.name, 'custom_attributes': {'plan': user.plan}, 'exp': int(time.time()) + 3600, } return jwt.encode(payload, secret, algorithm='HS256') ``` ### PHP (`firebase/php-jwt`) Asennus: `composer require firebase/php-jwt` ```php use Firebase\JWT\JWT; function sign_identity_token(string $secret, $user): string { $payload = [ 'user_id' => $user->id, 'email' => $user->email, 'name' => $user->name, 'custom_attributes' => ['plan' => $user->plan], 'exp' => time() + 3600, ]; return JWT::encode($payload, $secret, 'HS256'); } ``` ### Go (`golang-jwt`) Asennus: `go get github.com/golang-jwt/jwt/v5` ```go import ( "time" "github.com/golang-jwt/jwt/v5" ) func SignIdentityToken(secret string, user User) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "user_id": user.ID, "email": user.Email, "name": user.Name, "custom_attributes": map[string]any{"plan": user.Plan}, "exp": time.Now().Add(time.Hour).Unix(), }) return token.SignedString([]byte(secret)) } ``` ### Java (`jjwt`) Asennus (Maven): `io.jsonwebtoken:jjwt-api`, `jjwt-impl`, `jjwt-jackson` (runtime). ```java import io.jsonwebtoken.Jwts; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.Map; String signIdentityToken(String secret, User user) { SecretKeySpec key = new SecretKeySpec( secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); return Jwts.builder() .claim("user_id", user.getId()) .claim("email", user.getEmail()) .claim("name", user.getName()) .claim("custom_attributes", Map.of("plan", user.getPlan())) .expiration(new Date(System.currentTimeMillis() + 3_600_000)) .signWith(key) .compact(); } ``` ### Stripe-tilien välittäminen (tuleva ominaisuus) Kirjoittamalla `stripe_accounts`-taulukon JWT-tokeniin voit liittää käyttäjään Stripe-asiakastiedot. Varmennettu identiteetti toimii tulevien identiteettisidonnaisten toimintojen pohjana, jotka hakevat tilaukset ja laskut ilman erillistä tunnistautumista. ```javascript jwt.sign( { user_id: user.id, email: user.email, stripe_accounts: [{ label: 'Pääasiakkuus', stripe_id: 'cus_xxx' }], exp: Math.floor(Date.now() / 1000) + 3600, }, secret, { algorithm: 'HS256' }, ); ``` > **Tämä on tuleva ominaisuus:** > > Identiteettivarmistus ja `identity_verified`-lippu toimivat tänään. Toiminnot, > jotka lukevat `stripe_accounts`-tietoja (tilaukset, laskut) ovat erillinen > tuleva ominaisuus. Allekirjoita kenttä jo nyt, niin käyttöönotto on sujuvaa > kun ominaisuus julkaistaan. ## Identiteetin välittäminen widgetille Laske arvo palvelimella ja injektoi se sivulle. Kutsu sitten `identify`: ```javascript // Menetelmä A: HMAC-käyttäjätiiviste window.AihioWidget('identify', { user_id: currentUser.id, // 'externalId' hyväksytään myös aliaksena email: currentUser.email, name: currentUser.name, user_hash: '{{ palvelimelta_laskettu_tiiviste }}', }); // Menetelmä B: JWT-token window.AihioWidget('identify', { token: '{{ palvelimelta_allekirjoitettu_jwt }}', }); ``` `identify`-kutsun voi tehdä milloin tahansa `init`-kutsun jälkeen. Jos kutsut sitä ennen kuin bundle on latautunut, komento asetetaan jonoon ja suoritetaan automaattisesti käynnistyksen yhteydessä. Tyhjennä identiteetti uloskirjautumisen yhteydessä: ```javascript window.AihioWidget('resetUser'); ``` ## Salaisuuden kierrätys Salaisuuden kierrätys **ei** välittömästi mitätöi vanhaa salaisuutta. Edellinen salaisuus on voimassa 24 tuntia kierrätyksen jälkeen. Tämä vastaa Stripen webhook-salaisuuden kierrätysmallia: voit päivittää allekirjoituskoodin ympäristöihin ennen kuin vanha salaisuus vanhenee. 1. **Kierrätä salaisuus** Paina **Kierrätä salaisuus** kohdassa **Asetukset → Suojaus → Identiteetin varmistus**. 2. **Päivitä ympäristömuuttuja** Päivitä `AIHIO_IDENTITY_SECRET` palvelimesi ympäristöön uudella arvolla. 3. **Ota muutos käyttöön** Ota uusi ympäristömuuttuja käyttöön. Vanha salaisuus jatkaa istuntojen varmennusta rinnakkain 24 tunnin ajan. 4. **Odota 24 tuntia** 24 tunnin kuluttua vanha salaisuus lakkaa toimimasta. Holvimerkintä säilytetään, mutta sitä ei enää käytetä varmennuksessa. ## Pakota identiteettivarmistus Oletuksena varmistus on avoin (fail-open). Pakotuksen voi ottaa käyttöön chatbotin **Asetukset → Suojaus** -näkymästä. - **Pakota identiteettivarmistus.** Pyyntö, joka väittää identiteettiä (`user_id`/`external_id`, `email`, `token` tai `user_hash`) ilman validia allekirjoitusta, hylätään HTTP 403:lla. Anonyymit kävijät voivat silti keskustella. - **Vaadi tunnistautuminen (tiukka).** Jokainen varmistamaton pyyntö hylätään, myös anonyymit. Kun pakotus on päällä, keskustelu sidotaan varmennettuun `external_id`-tunnisteeseen. Uudelleenkäytetty `reference_id`, jonka tallennettu identiteetti ei vastaa varmennettua tokenia, aloittaa uuden keskustelun sen sijaan että liittyisi vanhaan. Näin yksi kävijä ei voi jatkaa toisen kävijän yksityistä keskustelua. Pakotus avautuu vasta, kun Aihio on nähnyt vähintään yhden validin tokenin, joten et voi lukita itseäsi ulos ottamalla sen käyttöön ennen kuin integraatiosi on toiminnassa. ## Turvallinen yhteys ja istunnon kesto - **Vain turvallinen yhteys.** Kun tämä on päällä, chat-widget lähettää identiteettiotsikot ja tallentaa identiteetin vain HTTPS-yhteydellä. Tavallisella HTTP-sivulla se ei lähetä identiteettiä, ja palvelin hylkää varmennuksen. - **Istunnon kesto.** Aseta tokenin suurin hyväksytty ikä (60 sekunnista 30 päivään). Chat-widget tyhjentää tallennetun identiteetin iän ylittyessä, ja palvelin hylkää rajaa vanhemman tokenin, vaikka sen oma `exp` olisi yhä voimassa. ## Käyttöönottotapa: toiminto avoimena (fail-open) Nykyinen toteutus on **avoin käyttöönottoa varten**: jos allekirjoitusta ei toimiteta tai varmennus epäonnistuu, istunto jatkuu `identity_verified = false` -tilassa eikä sitä hylätä. Tämä tarkoittaa, että varmistamaton `user_id` tai sähköposti hyväksytään näyttövihjeinä, mutta teknisesti hyökkääjän muutettavissa olevia arvoja. Käsittele identiteettitietoja luotettavina **vain** kun keskustelun `identity_verified`-lippu on `true`. ## Allekirjoitetut vs allekirjoittamattomat tiedot Vain JWT:n sisällä allekirjoitetut tiedot ovat varmennettuja ja luotettavia. Kaikki muu on näyttövihje, jonka lähettäjä voi väärentää. | Allekirjoitettu (luotettava) | Allekirjoittamaton (vain vihje) | | ---------------------------------------------------------------- | ----------------------------------------------- | | `user_id` / `external_id` | Prechat-lomakkeen nimi, sähköposti ja suostumus | | `email`, `name`, `custom_attributes` (kun ne ovat JWT:n sisällä) | Mikä tahansa `x-identify`-otsikko ilman JWT:tä | HMAC-käyttäjätiiviste (tapa A) varmentaa vain `external_id`-arvon. Jos haluat luottaa `email`-, `name`- tai mukautettuihin kenttiin, allekirjoita ne JWT:n sisällä (tapa B). Lähettäjän antamat kentät hyväksytään näyttövihjeinä. Niitä ei koskaan tallenneta varmennettuina, ja keskustelun `identity_verified`-lippu pysyy arvossa `false`, ellei validia allekirjoitusta ole. ## Tietoturvatarkistuslista - Tallenna chatbotin salaisuus yksinomaan palvelimelle. Älä sisällytä sitä selainpuolen JavaScript-koodiin tai versiohallintaan. - Aseta `exp` jokaiseen JWT-tokeniin (suositus 1 h). - Kierrätä salaisuus heti, jos se paljastuu (**Asetukset → Suojaus → Identiteetin varmistus**). - Tarkista `identity_verified = true` toimintokäsittelijöissäsi ennen tilitasoisen datan palauttamista. - Käytä HMAC-funktiosi oletustulosteen pieniä kirjaimia sisältävää heksadesimaalia. --- # Julkaisu URL: https://aihio.ai/ohjekeskus/tekoalyagentti/julkaisu Kuvaus: Chatbotin julkaiseminen sivustolle. Julkaisemiseen kuuluu koodinpätkän asentaminen sivustolle ja chatbotin aktivointi. ## Ennen julkaisua Käy testi-keskustelu esikatselussa ja varmista, että chatbot vastaa odotetulla tavalla eri kysymyksiin. ## Koodinpätkän asennus Avaa ohjauspaneelissa chatbotin **Muokkain** ja valitse oikeasta yläkulmasta **Asenna → Verkkosivun chat-widget**. Kopioi `