Integrationsvejledning
Guide til chatwidget
Din chatbot svarer besøgende på dit eget websted. To linjer sætter den der; alt det andet er valgfrit.
Afsnit 1 til 5 er hele arbejdet for de fleste websteder. Afsnit 6 og 7 er parret, der lader din chatbot svare på „hvor er min ordre?“ — de kræver en udvikler og en eftermiddag. Resten er opslag, politik og hvad du gør, når noget er galt.
01Dit chat-ID
Ét id identificerer din chatbot. Du finder det i kodestykket under Kode til dit websted på fanen Hjem på din chatside, ved siden af Kopiér-knappen.
Det identificerer din chatbot og intet andet: ingen konto, ingen API-nøgle, intet login nogen steder på dit websted.
Må gerne offentliggøres. Det ligger i din sides HTML, hvor alle kan læse det. Det eneste, det kan, er at stille din chatbot et spørgsmål — og kun på de websteder, du har tilladt.
02Sæt den på dit websted
Tilføj begge linjer på en vilkårlig side, lige før det afsluttende body-tag.
<script src="https://www.obbo.me/obbochat.js" async></script>
<div data-obbo-chat="YOUR_CHAT_ID"></div>Scriptet indlæses én gang pr. side og sætter en chatboble i hjørnet. Læg det i dit websteds skabelon, så har hver side den.
Tillad dit websted først.Din chatbot svarer kun på de websites, der står under fanen Navn og velkomst på chatsiden i dit Brand Center – til at begynde med dit Brand Centers eget website. Tilføj alle andre websites, der viser chatten, fx en testkopi. På andre websites afslår den høfligt, og chatsiden viser, hvilket website der prøvede.
Kommer koden efter sideindlæsningen? Scriptet leder efter den én gang, når siden er klar. Hvis dit websted indsætter chatten senere — en single-page-app, en tag manager, et samtykkebanner — så kald ObboChat.mount() bagefter.
03Sådan ser den ud
Vælg udseendet under Udseende i din chatbots indstillinger, og kopiér koden: den skriver disse attributter og stilværdier for dig. Kun chat-id’et er påkrævet, og resten kan du rette i hånden.
<script src="https://www.obbo.me/obbochat.js" async></script>
<div data-obbo-chat="YOUR_CHAT_ID"
data-obbo-title="Acme Help"
data-obbo-position="left"
data-obbo-launcher="Ask us a question"
style="--obbo-accent: #0f766e; --obbo-accent-text: #ecfdf5; --obbo-bottom: 96px"></div>| Attribut | Hvad det er |
|---|---|
| data-obbo-chat | Dit chat-ID. Påkrævet — det er det, der vælger din chatbot. |
| data-obbo-title | Panelets overskrift, som regel dit firmanavn. |
| data-obbo-theme | "dark" for et mørkt panel. Standarden er lys. |
| data-obbo-position | "left" for at placere boblen nederst til venstre. Standarden er højre. |
| data-obbo-launcher | Tekst på chatknappen, f.eks. "Spørg os". Uden den er knappen et rundt ikon. |
| data-obbo-icon | "lines" eller "dots" for en chatknap, hvis boble har linjer eller prikker. Standard er en enkel boble. |
| data-obbo-font | "site" for at bruge din sides egen skrifttype. Standarden er en almindelig systemskrift. |
| style | Udseendets stilværdier: brandfarve, tekstfarver, afstand fra kanterne, hjørner og vinduesstørrelse. Se Egne stilarter nedenfor. |
Har du ændret udseendet? Kopiér koden igen. Udseendet ligger i koden, så dit website beholder det gamle, indtil du erstatter den.
Farver
Farvetabellen sætter seks farver: en baggrund og en tekstfarve for dit brand, for chatbottens svar og for chatvinduet. Dit brand dækker alt, hvad en besøgende trykker på eller sender: chatknappen, Send-knappen, de foreslåede spørgsmål og deres egne beskeder. Chatvinduet dækker titlen, skrivefeltet, links og småt, og linjerne er en svag udgave af teksten. Tryk på et felt for at åbne farvevælgeren. Match mit website sætter din brandfarve og viser det, den fandt på din forside, som logoets farve, øverst i hver farvevælger.
Start fra Lys eller Mørk sætter alle farver, der står på Automatisk. Automatisk tekst er hvid eller sort, alt efter hvad der læses bedst på baggrunden. Hvert tekstfelt viser, hvor let det er at læse, og når en farve er svær at læse, tilbyder vælgeren Automatisk igen.
Egne stilarter
Hver farve, størrelse og afstand i widgetten læser sin egen stilværdi. Sæt en hvilken som helst af dem på chattens div eller i dit eget stylesheet, og dine går forud for dem i koden.
[data-obbo-chat] {
--obbo-accent: #0f766e;
--obbo-assistant-text: #1f2937;
--obbo-font: "Inter", sans-serif;
--obbo-radius: 0;
--obbo-bottom: 96px;
}| Stilværdi | Hvad den ændrer |
|---|---|
| --obbo-accent --obbo-accent-text | Chat- og Send-knapperne, foreslåede spørgsmål, besøgendes beskeder og teksten på dem |
| --obbo-panel-bg --obbo-text --obbo-border | Chatvinduets baggrund, tekst, skrivefelt, links og linjer |
| --obbo-assistant-bg --obbo-input-bg | Svarboblerne og skrivefeltet |
| --obbo-visitor-text --obbo-assistant-text | Tekst i den besøgendes beskeder og i chatbottens svar |
| --obbo-radius --obbo-launcher-radius --obbo-chip-radius | Hvor runde hjørnerne er: vinduet, knappen og svarknapperne |
| --obbo-font --obbo-font-size | Skrifttype og tekststørrelse |
| --obbo-width --obbo-height | Størrelsen på det åbne chatvindue |
| --obbo-bottom --obbo-side | Afstand fra bunden og fra den valgte side |
| --obbo-shadow --obbo-z | Skygge, og hvad den ligger foran |
Hvad der ligger fast
Panelets egne ord — hilsenen, send-knappen, bedømmelsen — er på engelsk i dag, og opbygningen er vores. Vil du have din egen kode hele vejen, så byg din egen brugerflade med den headless motor nedenfor — samme chatbot, din HTML og din CSS.
Svar har ét sprog. Din chatbot svarer på dit indholdscenters sprog, uanset hvilket sprog en besøgende skriver på: din virksomhed med sine egne ord frem for en oversættelse af dem.
04Byg din egen grænseflade
Boblen er valgfri. Samme script udstiller en headless motor: send spørgsmål, abonnér på samtalens tilstand, og vis den, som dit websted gør.
<script src="https://www.obbo.me/obbochat.js" async></script>
<script>
addEventListener('load', function () {
var chat = ObboChat.createChat({ widgetId: 'YOUR_CHAT_ID' });
chat.subscribe(function (state) {
// state.turns is the conversation; state.busy while answering.
});
chat.ask('What do you sell?');
});
</script>| Kald | Hvad det er |
|---|---|
| createChat({ widgetId }) | Én motor pr. chatbot; returnerer håndtaget nedenfor. |
| ask(text) | Send ét besøgsspørgsmål. Afsluttes, når svaret er landet i tilstanden. |
| subscribe(fn) | Kaldes med samtalens tilstand nu og ved hver ændring; returnerer en afmeldingsfunktion. |
| getState() | Samtalens aktuelle tilstand: replikkerne og om et spørgsmål er undervejs. |
| startEstimate() | Start et tilbud, ligesom et tryk på Få et tilbud: efter velkomsten, og ikke igen mens et er i gang, eller når chatbotten ikke giver tilbud. |
| destroy() | Kobler alle lyttere fra. Kald den, når dit framework fjerner grænsefladen. |
| ObboChat.mount() | Led efter kodestykket på siden igen, når koden kom sent. |
| ObboChat.startEstimate(id?) | Åbn en chat på siden direkte i et tilbud: chatten med det ID, du angiver, eller den første. Returnerer false, når der ikke er nogen. |
Én motor pr. chatbot. Alt det boblen gør — samtalen indtil nu, et foto til et prisoverslag, en indlogget besøgende — kommer til dig i den tilstand, du abonnerer på, så dit eget panel går ikke glip af noget.
05Prisoverslag
Din chatbot kan give en pris inde i samtalen, ud fra de spørgsmål og priser du opsætter.
Har din chatbot en prisberegner, bliver en besøgende, der spørger, hvad noget koster, bedt om at beskrive opgaven med få ord eller tilføje et foto. Fotoet læses op imod dine egne spørgsmål i beregneren – størrelsesintervallet, et antal, et omtrentligt spænd for en målt mængde – og det, der afgøres, vises for den besøgende til bekræftelse eller rettelse, før det tæller. Intet prissættes ud fra et foto alene: et tal fastsættes altid af den besøgende, og deres ord vejer tungere end billedet.
Hvad den besøgende får
Et specificeret prisinterval i din valuta med en linje for hver del af opgaven – eller, hvis du vælger det, ét beløb og hvad det bygger på – og din egen tekst under hvert overslag. Det er et overslag og aldrig et tilbud: beregningen bygger på dine priser, og det, dine priser ikke dækker, står som prissat separat.
Hvad du selv opsætter
Fem korte trin under Pristilbud på din chatside, og intet i kode:
- Scan mit websted: dine priser, ekstra gebyrer og vilkår læses fra de sider, din chatbot allerede kender, og de spørgsmål, der ændrer beløbet, foreslås for dig.
- Prisliste: alle priser i ét ark. Ret en celle, eller download arket, ret det i Excel eller Google Sheets, og upload det igen. Priser, der er regnet ud for dig, er markeret som Skøn, indtil du har tjekket dem.
- Dine overslag: et eksempel på en opgave præcis, som en besøgende ser den. Ret, fjern eller tilføj en hvilken som helst linje, og vælg, hvad besøgende ser: hver linje eller ét beløb og hvad det bygger på.
- Spørgsmål: det, en besøgende bliver spurgt om, i din rækkefølge: vælg én, vælg flere, et tal, en dato, ja eller nej, ord eller et foto. Hvert spørgsmål viser, hvad svaret gør ved beløbet.
- Gebyrer og grænser: ekstra gebyrer og grænser, teksten under hvert overslag, hvor store opgaver du hellere selv vil give pris på, og kontakten, der slår overslag til.
Tallene er stadig dine. Hver pris, der er regnet ud for dig, er markeret som Skøn i din Prisliste, og hvert overslag under Kundesamtaler viser, hvor meget der kom fra typiske takster frem for dine egne priser. Et interval, du ikke ville stå inde for, er synligt i stedet for skjult.
Hvor overslagene lander
Hvert overslag vises under Kundesamtaler med de svar, det blev lavet ud fra – hvert markeret som læst fra den besøgendes ord, fra deres foto eller slået op – og med kontaktoplysninger, når de har efterladt dem. Du kan også få hvert færdigt overslag tilsendt på e-mail.
Fotos opbevares i 90 dage og kan slettes der, ét ad gangen. Hvert overslag noterer også, hvad et foto viste, som ingen spørgsmål spurgte om.
Intet at bygge. Både boblen og den headless motor har prisoverslaget med — din egen brugerflade får hvert overslag i samtalens tilstand — så der er ikke noget at bygge her.
Et “Få et tilbud”-link på dit website
Enhver knap eller ethvert link på en side med din chat kan åbne den direkte i et tilbud. Indsæt dette, hvor du vil have det, og giv det samme udseende som resten af dit website; området Hent koden på din chatside har det klar til at kopiere.
<a href="#get-estimate" data-obbo-estimate>Get an estimate</a>Linket virker også alle andre steder fra — en anden side, en e-mail, en annonce eller et opslag på sociale medier. Tilføj #get-estimate til sidst i adressen på en side med din chat, så åbner chatten i et tilbud, når siden indlæses.
Din egen knap. Sæt data-obbo-estimate på et hvilket som helst element, eller kald ObboChat.startEstimate() fra din egen kode. En besøgende med et igangværende tilbud vender tilbage til det i stedet for at starte forfra, og en chatbot uden aktiv tilbudsberegner åbner bare.
06Fortæl chatten, hvem der er logget ind
Din server indestår for, hvem den besøgende er, og din chatbot svarer med personens egne oplysninger for øje.
Hvis besøgende logger ind på din side, kan chatten svare på spørgsmål om deres egen konto – »hvor er min ordre?« – ud fra de oplysninger, din side vælger at dele. Slå Indloggede besøgende til på chatsiden i dit Brand Center for at få din besøgsnøgle.
Din besøgendenøgle er en serverhemmelighed.Din besøgsnøgle hører kun hjemme på din server. Læg den aldrig i en webside, en app eller et kodearkiv. Hvis den slipper ud, så udskift den på chatsiden, og vælg at stoppe den gamle nøgle med det samme.
Sådan hænger det sammen
| Hvem | Hvad de gør |
|---|---|
| Din server | Signerer et kort token om den indloggede besøgende med din besøgsnøgle. |
| Din side | Giver tokenet til chatten og fortæller, når den besøgende logger ud. |
| Chatten | Kontrollerer signaturen og udløbet og svarer derefter med den besøgendes oplysninger for øje. Et token, der ikke består kontrollen, ignoreres, og den besøgende får svar som gæst. |
Hvad tokenet indeholder
| Felt | Hvad det er |
|---|---|
| sub | Påkrævet. Dit eget ID for den besøgende. |
| name | Valgfrit. Hvad de skal kaldes. |
| account | Valgfrit. Alt det, du vælger at dele – ordrer, bookinger, abonnement – som et JSON-objekt på op til 6 KB. |
| exp | Påkrævet. Udløb i Unix-sekunder, højst en time frem. Ti minutter er rigeligt: chatten beder din side om et nyt token, når det udløber. |
| iat | Valgfrit. Hvornår du udstedte det, i Unix-sekunder. |
Signér det som en JWT med HS256 med din besøgsnøgle som hemmelighed. Ethvert JWT-bibliotek kan det. I Node:
// Your server (Node + Express). npm install jsonwebtoken
import jwt from 'jsonwebtoken';
app.post('/chat-token', (req, res) => {
res.set('Cache-Control', 'no-store');
const user = req.user; // your own sign-in
if (!user) return res.json({ token: null });
const token = jwt.sign(
{
sub: String(user.id),
name: user.firstName,
account: { orders: recentOrdersFor(user) }, // whatever you choose to share
},
OBBO_VISITOR_KEY, // from your server's secret settings
{ algorithm: 'HS256', expiresIn: '10m' },
);
res.json({ token });
});I Python:
# Your server (Python + Flask). pip install PyJWT
import time
import jwt
@app.post("/chat-token")
def chat_token():
user = current_user() # your own sign-in
if user is None:
return {"token": None}
now = int(time.time())
claims = {
"sub": str(user.id),
"name": user.first_name,
"account": {"orders": recent_orders_for(user)}, # whatever you choose to share
"iat": now,
"exp": now + 600,
}
return {"token": jwt.encode(claims, OBBO_VISITOR_KEY, algorithm="HS256")}På din side
Giv chatten en funktion, der henter et token fra din server. Chatten kalder den kun, når der stilles et spørgsmål, og den ikke har et gyldigt token – aldrig bare fordi en side blev indlæst.
<script>
addEventListener('load', function () {
ObboChat.identify(async function () {
const res = await fetch('/chat-token', { method: 'POST' });
return (await res.json()).token;
});
});
// When the visitor signs out: ObboChat.identify(null);
</script>Hvis din side logger besøgende ind med en cookie, klarer én attribut det samme: chatten sender en POST til den adresse på din egen side. Lad ruten kun acceptere POST, tjekke dit eget login, ikke sende CORS-headere og svare med et tomt token, når ingen er logget ind.
<script src="https://www.obbo.me/obbochat.js" async></script>
<div data-obbo-chat="YOUR_CHAT_ID" data-obbo-identity-url="/chat-token"></div>Når en besøgende logger ud, så kald ObboChat.identify(null). Chatten rydder deres samtale med det samme, så den næste person ved samme computer starter forfra.
Tjek, at det virker
Hvert chatsvar fortæller din side, hvad der skete med tokenet: visitor.status er signed_in, anonymous eller refused, og et afvist token har en årsag med. Chatsiden i dit Brand Center viser også det seneste accepterede og afviste token.
| Årsag | Hvad det betyder |
|---|---|
| bad_signature | Ikke signeret med din nuværende besøgsnøgle (eller med en udskiftet nøgle, hvis frist er udløbet). |
| expired | Forbi sit exp. |
| not_yet_valid | Dets iat eller nbf ligger i fremtiden. Tjek din servers ur. |
| too_long_lived | Dets exp ligger mere end en time frem. |
| invalid_claims | sub eller exp mangler, eller et felt har forkert type eller længde. |
| too_large | Tokenet eller dets kontooplysninger er over størrelsesgrænsen. |
| malformed | Ikke en gyldigt opbygget JWT. |
| unsupported_algorithm | Signeret med noget andet end HS256. |
| not_enabled | Indloggede besøgende er slået fra for denne chatbot. |
| keys_unavailable | Vi kunne ikke kontrollere din nøgle lige da. Der er intet at rette hos dig. |
Prøv det, før du går live. På fanen Prøv den på din chatside vælger du „en indlogget kunde“, skriver et navn og de kontooplysninger, dit websted ville sende, og testchatten spørger med et rigtigt token signeret med din nøgle — kontrolleret præcis som en besøgendes.
Hvad vi gemmer.Kontooplysningerne sendes til vores AI-udbyder for at skrive hvert svar. De gemmes ikke sammen med dine samtaler eller i vores logs, men et svar kan gentage dem.
07Svar på spørgsmål om en konto
„Hvor er min ordre?“ — besvaret fra dine egne systemer.
Din chat kan svare på spørgsmål om den besøgendes egen konto — hvor deres ordre er, om en betaling gik igennem — ved at spørge ét endepunkt på din egen server. Vi forbinder aldrig til dine systemer og har ingen nøgle til dem: vi sender en beskrivelse af, hvad ét spørgsmål kræver, og du bestemmer, hvad der returneres.
- Slå indloggede besøgende til (afsnit 6). Din chatbot kan kun spørge om en person, dit eget websted har navngivet.
- Byg ét endepunkt på din server. Et GET returnerer dit katalog; et POST svarer på én forespørgsel hentet fra det.
- På fanen Navn og velkomst på din chatside åbner du For udviklere, og under Spørgsmål om kontoen indsætter du adressen i Dit dataendepunkt og trykker Forbind adresse – så læser den med det samme, hvad den kan svare på.
- Tryk Slå til. Derefter viser panelet, hvor mange opslag der er lavet, hvor lang tid dit endepunkt tager, og det seneste problem i almindelige ord.
Afgræns hvert svar til tokenet.Afgræns hvert svar til sub i det besøgstoken, du har verificeret med din egen nøgle, og ignorér enhver identitet, der måtte fremgå af resten af forespørgslen. Det er det, der gør det umuligt at vise én besøgende en andens data.
Beskriv dine data
Et GET til dit endepunkt returnerer dit katalog: de typer data, du kan returnere, og felterne i hver. Derfor skal du aldrig liste de spørgsmål, dine besøgende kunne stille — chatten matcher selv spørgsmålet mod dit katalog, når det stilles.
// GET https://your-site.com/obbo-chat/data
// Describe the data you have. You never list the questions your visitors
// might ask — the chat matches a question to one of these itself.
{
"version": 1,
"resources": [{
"name": "orders",
"title": "Orders",
"description": "Orders on this customer's account, newest first.",
"scope": "visitor",
"fields": [
{"name": "status", "type": "string", "description": "Where it stands.",
"values": ["paid", "shipped", "delivered"]},
{"name": "ship_date", "type": "date", "description": "When it shipped."},
{"name": "tracking", "type": "string", "description": "Carrier tracking link."}
],
"filters": [{"field": "status", "ops": ["eq"]}],
"default_request": {"fields": ["status", "ship_date", "tracking"], "limit": 10},
"max_limit": 25
}]
}| Felt | Hvad det er |
|---|---|
| name | Identifikator i små bogstaver for denne slags data. Forespørgsler nævner den. |
| title | Hvad det skal kaldes i et svar. |
| scope | visitor — det eneste scope der findes: alt handler om den, der spørger. |
| description | Hvad det indeholder, i én sætning. Spørgsmål matches mod denne, så skriv den, som dine besøgende taler. |
| fields[] | Hvert felt: et navn, en type, en kort beskrivelse og dets values, hvor de er et fast sæt. |
| filters[] | Hvilke felter der må filtreres, og med hvilke operatorer: eq, neq, gte, lte, contains, in. |
| default_request | De felter og den grænse, der bruges, når spørgsmålet ikke siger mere. |
| max_limit | Det højeste antal elementer, du nogensinde returnerer ad gangen. |
Besvar én forespørgsel
Et POST bærer én struktureret forespørgsel hentet fra dit katalog og det besøgendetoken, din egen server signerede. Alt i den kommer fra dit katalog — en forespørgsel, dit katalog ikke beskriver, bliver aldrig sendt.
// POST https://your-site.com/obbo-chat/data
// "Where is my order?" becomes this. Operators are only:
// eq, neq, gte, lte, contains, in. There is no SQL and no free text.
{
"version": 1,
"visitor_token": "<the token YOUR server signed for this visitor>",
"request": {
"resource": "orders",
"filters": [{"field": "status", "op": "eq", "value": "shipped"}],
"fields": ["status", "ship_date", "tracking"],
"limit": 10
},
"request_id": "chat_9f2c41b8"
}Svar med netop den del og intet andet. Hold svaret under 64 KB.
// Answer with that slice, and nothing else.
{
"version": 1,
"resource": "orders",
"items": [
{"status": "shipped", "ship_date": "2026-09-14",
"tracking": "https://carrier.example/track/4471"}
],
"item_count": 1,
"truncated": false
}| Felt | Hvad det er |
|---|---|
| version | Altid 1. |
| resource | Den ressource, du svarede om. |
| items[] | Rækkerne — hver med kun de felter, der blev bedt om. Alt, du sender her, kan blive læst op for den besøgende. |
| item_count | Hvor mange rækker der er i items. |
| truncated | true, hvis du holdt nogle tilbage. |
Tjek af en forespørgsel
To kontroller, i denne rækkefølge: at forespørgslen er signeret med din besøgendenøgle, og hvem den handler om. Begge bruger den samme nøgle, du allerede signerer besøgendetokens med — der er intet nyt at installere. Begge kald er signerede, GET’et over en tom body.
| Header | Hvad det er |
|---|---|
| X-Chat-Data-Signature | Tidsstempel og signatur. t er unix-sekunder, og v1 er HMAC-SHA256 i hex som nedenfor. Accepter den kun inden for fem minutter af t. |
| X-Chat-Key-Version | Hvilken af dine nøgler der signerede. Den skifter, når du udskifter din nøgle, og i karensdøgnet ankommer begge versioner. |
X-Chat-Data-Signature: t=1760000000,v1=4b1f…9c // hex, 64 chars
X-Chat-Key-Version: 3
signature = HMAC_SHA256(
your_visitor_key,
"<t>.<METHOD> <path and query>." + <raw request body>
).hex()
// GET "1760000000.GET /obbo-chat/data." + ""
// POST "1760000000.POST /obbo-chat/data." + the bytes that arrivedconst crypto = require('crypto');
const KEY = process.env.OBBO_VISITOR_KEY; // the key from your chat page
// 1. Is this really from the chat service? The signature covers the method,
// the path as sent, and the RAW body — so check before you parse.
function fromObbo(req, rawBody) {
const fields = Object.fromEntries(
(req.get('X-Chat-Data-Signature') || '').split(',').map(p => p.trim().split('='))
);
const t = Number(fields.t);
if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const mine = crypto.createHmac('sha256', KEY)
.update(`${t}.${req.method} ${req.originalUrl}.`)
.update(rawBody)
.digest('hex');
const a = Buffer.from(mine), b = Buffer.from(fields.v1 || '');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// 2. Who is it about? From the token YOU signed — never from the body.
function visitorId(token) {
const [head, payload, sig] = String(token || '').split('.');
if (!head || !payload || !sig) return null;
const mine = crypto.createHmac('sha256', KEY)
.update(`${head}.${payload}`).digest('base64url');
if (mine !== sig) return null;
const claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
if (!claims.exp || Date.now() / 1000 > claims.exp) return null;
return claims.sub;
}
// Your catalog. Signed too, over an empty body.
app.get('/obbo-chat/data', (req, res) => {
if (!fromObbo(req, '')) return res.sendStatus(403);
res.set('Cache-Control', 'no-store').json(CATALOG);
});
// One request. express.text keeps the raw bytes the signature covers;
// express.json would re-encode them and the check would fail.
app.post('/obbo-chat/data', express.text({ type: '*/*' }), async (req, res) => {
if (!fromObbo(req, req.body)) return res.sendStatus(403);
const body = JSON.parse(req.body);
const customer = visitorId(body.visitor_token);
if (!customer) return res.sendStatus(403);
const asked = body.request || {};
if (asked.resource !== 'orders') return res.sendStatus(422); // not mine to answer
// Scope EVERY query by `customer`. Ignore any id in the request body.
const rows = await db.orders.findAll({
where: { customer_id: customer, ...where(asked.filters) },
limit: Math.min(asked.limit || 10, 25),
});
// Return the fields that were asked for, and never a field you did not
// declare: everything you send is read out to the visitor.
const items = rows.map(row => pick(row, asked.fields));
res.set('Cache-Control', 'no-store')
.json({ version: 1, resource: 'orders', items, item_count: items.length });
});Hvad du skal svare
Din statuskode er det, din chatside rapporterer til dig, så brug dem præcis sådan:
| Svar | Hvad det betyder |
|---|---|
| 200 | Her er data. En tom items-liste er et helt godt svar — din chatbot siger, at den ikke fandt noget på kontoen. |
| 403 | Signaturen eller besøgendetokenet holdt ikke. Svar ens ved enhver afvisning; sig aldrig hvilken kontrol der fejlede. |
| 404 | Der er intet, der lytter. Din chatside læser det som en forkert adresse. |
| 422 | Dit katalog beskriver forespørgslen, men du vil ikke betjene den. Din chatbot svarer uden den, og den besøgende mærker ingen fejl. |
| 500 | Noget gik i stykker hos dig. Tælles som en fejl. |
| 301 / 302 | Følges aldrig — giv os den endelige adresse i stedet. |
Fem fejl i træk, og vi holder op med at spørge. Opslag holder pause i fem minutter, og din chatside siger det med den seneste årsag. De starter igen af sig selv; et opslag, der lykkes, nulstiller tælleren, mens Læs hvad det kan svare på kun tester endepunktet. Din chatbot svarer på alt andet hele tiden.
Langsom tæller som slukket.En forespørgsel besvares inden for 2,5 sekunder, ellers svarer chatten uden den — den besøgendes spørgsmål fejler aldrig, fordi dit endepunkt var langsomt.
08Grænser og tider
Alt, kontrakten sætter grænser for, samlet på ét sted.
| Grænse | Hvad den gælder |
|---|---|
| 2.5 s | Dit endepunkts tid til én forespørgsel. Tager det længere, svarer din chatbot uden det. |
| 5 s | Dit endepunkts tid, når dit katalog læses. |
| 8 KB | Den største forespørgselsbody, vi sender. |
| 64 KB | Det meste, vi vil læse af dit svar. |
| 8 KB | Hvor meget af dit svar der når frem i svaret — hele elementer, fjernet fra enden indtil resten passer. |
| 32 KB | Det største katalog, vi vil læse. |
| 12 | Slags data i ét katalog. |
| 40 | Felter i én slags data. |
| 10 | Filtre i én forespørgsel. |
| 120 | Tegn i én filterværdi. |
| 10 | Værdier i et in-filter. |
| 6 KB | Kontooplysninger i et besøgendetoken. |
| 1 h | Det længste et besøgendetoken må leve. Ti minutter er rigeligt. |
| 5 | Fejl i træk, før opslag holder pause. |
| 5 min | Hvor længe pausen varer. |
09Content Security Policy
Sætter dit websted en CSP, så tillad disse to. Der skal ikke mere til: ingen rammer, ingen skrifttyper, ingen billeder fra os.
| Direktiv | Hvorfor |
|---|---|
| script-src https://www.obbo.me | Indlæser widget-scriptet. |
| connect-src https://www.obbo.me | Chatspørgsmålene og svarene. |
Stilværdierne i koden sættes af widgettens script, så en politik, der blokerer inline-stilarter, bevarer dit udseende. Browseren kan stadig skrive en advarsel om style-attributten; den er harmløs.
Hvem bliver identificeret.Gæster identificeres aldrig: en tilfældig sessionsnøgle i deres browser samler én besøgendes spørgsmål i en samtale på dit dashboard, og det er alt, den gør. Indloggede besøgende identificeres kun ved det ID og navn, din side sender.
10Når den ikke virker
Der kommer slet ingenting
Tjek, at div’en har data-obbo-chat med dit chat-id, og at script-tagget er på siden. Kommer koden efter at siden er klar, så kald ObboChat.mount() én gang bagefter. Din chatside nævner også det seneste websted, chatten blev set virke på, hvilket afgør, om kodestykket overhovedet er live.
Den siger, den ikke kan svare på dette websted
Webstedet står ikke på din tilladte liste. Tilføj det på fanen Navn og velkomst på din chatside — det afviste websted står navngivet der, så du kan se, at det virkelig er dit.
Den bliver ved med at sige, at den ikke er sikker
Din chatbot svarer ud fra det, den har læst af dit websted, og intet andet. Se fanen Dit websted, udgiv efter en ændring, og læs fanen Samtaler: hvert spørgsmål en besøgende har stillet står der, og de spørgsmål den ikke kunne svare på er de sider, der er værd at skrive.
Mine besøgendetokens bliver afvist
Hvert svar siger hvorfor: visitor.status er refused, med en af årsagerne i afsnit 6. De to sædvanlige er et serverur, der er uenigt med vores, og en nøgle, der kun blev udskiftet i den ene ende. Din chatside viser den seneste afvisning og dens årsag.
Kontospørgsmål siger, at mit endepunkt ikke kunne nås
Din chatside nævner problemet i almindelige ord. Er det signaturen, så tjek tre ting: at den dækker den rå body og ikke genkodet JSON, at stien indeholder sin query-streng, og at metoden er med store bogstaver.
I er holdt op med at spørge mit endepunkt
Fem fejl i træk sætter opslag på pause i fem minutter, og de starter igen af sig selv. Ret årsagen: det næste opslag, der lykkes, nulstiller tælleren.
11Spørgsmål?
Giv os sideadressen, dit chat-id og det spørgsmål du stillede, så ser vi på præcis det, du ser på. Vi læser alt, der sendes til hello@obbo.me