[{"data":1,"prerenderedAt":3726},["ShallowReactive",2],{"pomoc:\u002Fhelp\u002Ftools\u002Fcustom-webhook-tools":3,"pomoc-pages:help_en":3375},{"id":4,"title":5,"alt":6,"body":7,"description":3367,"extension":3368,"key":3369,"meta":3370,"navigation":1360,"order":1357,"path":3371,"seo":3372,"stem":3373,"__hash__":3374},"help_en\u002Fhelp\u002Ftools\u002Fcustom-webhook-tools.md","Custom webhook tools","\u002Fpomoc\u002Fnarzedzia\u002Fwlasne-webhooki",{"type":8,"value":9,"toc":3340},"minimark",[10,28,53,58,61,65,72,177,187,191,198,328,332,335,393,398,412,415,422,443,453,457,468,510,514,524,891,894,900,916,1037,1046,1057,1087,1091,1146,1152,1165,1169,1192,1296,1300,1318,3179,3183,3186,3193,3204,3219,3226,3231,3235,3256,3260,3316,3320,3336],[11,12,15],"callout",{"title":13,"type":14},"Experimental feature","warning",[16,17,18,19,23,24,27],"p",{},"Custom webhooks are in controlled rollout. If you don't see the\n",[20,21,22],"strong",{},"Custom webhooks"," card under ",[20,25,26],{},"Settings → Integrations",", ask your Fibly contact to enable it\nfor your workspace.",[16,29,30,32,33,38,39,43,44,47,48,52],{},[20,31,22],{}," let you turn any HTTP API you already run into a tool your bot can call\nduring a conversation — checking an order's status, looking up a customer record, opening a\nsupport ticket, or anything else your backend can do over HTTP. Unlike the ready-made\nintegrations (",[34,35,37],"a",{"href":36},"\u002Fhelp\u002Ftools\u002Fcalendly","Calendly",", ",[34,40,42],{"href":41},"\u002Fhelp\u002Ftools\u002Fprestashop","PrestaShop","), this is a\n",[20,45,46],{},"self-serve, no-Fibly-engineering"," way to connect ",[49,50,51],"em",{},"your own"," systems — but it does need a\ndeveloper on your side to build the receiving endpoint and, for the parts covered in this guide,\nto verify that a call genuinely came from Fibly.",[54,55,57],"h2",{"id":56},"when-to-use-it","When to use it",[16,59,60],{},"Reach for a custom webhook tool when you want the bot to call a system that has no dedicated\nFibly integration: your own order management API, an internal ticketing system, a booking\nbackend, a CRM, anything reachable over HTTPS. If the data or action already fits an existing\nintegration (Calendly, PrestaShop), use that instead — it needs no code on your end.",[54,62,64],{"id":63},"building-a-connection","Building a connection",[16,66,67,68,71],{},"A ",[20,69,70],{},"connection"," is one of your backend services: its base URL and the static credentials the\nbot uses to call it. Every tool you define lives under a connection and calls a path relative to\nits base URL.",[73,74,75,89,95,174],"ol",{},[76,77,78,79,81,82,84,85,88],"li",{},"Open ",[20,80,26],{},", find the ",[20,83,22],{}," card, and click ",[20,86,87],{},"Manage",".",[76,90,91,92,88],{},"Click ",[20,93,94],{},"Add connection",[76,96,97,98],{},"Fill in:\n",[99,100,101,107,122,159],"ul",{},[76,102,103,106],{},[20,104,105],{},"Name"," — a label for your own reference (e.g. \"Order lookup API\").",[76,108,109,112,113,117,118,121],{},[20,110,111],{},"Base URL"," — your API's root, e.g. ",[114,115,116],"code",{},"https:\u002F\u002Fapi.example.com",". It must be a publicly\nreachable ",[20,119,120],{},"HTTPS"," URL — local or private addresses, and unencrypted (non-HTTPS) traffic,\nare rejected.",[76,123,124,127,128,131,132],{},[20,125,126],{},"Authentication"," — how Fibly authenticates to ",[49,129,130],{},"your"," API:\n",[99,133,134,140,150],{},[76,135,136,139],{},[20,137,138],{},"No authentication"," — no credentials sent.",[76,141,142,145,146,149],{},[20,143,144],{},"Basic auth"," — a username and password, sent as an ",[114,147,148],{},"Authorization: Basic"," header.",[76,151,152,155,156,88],{},[20,153,154],{},"Bearer token"," — a single token, sent as ",[114,157,158],{},"Authorization: Bearer \u003Ctoken>",[76,160,161,164,165,168,169,173],{},[20,162,163],{},"Send verification token"," — off by default. Enable this if you want Fibly to send the\nsigned ",[114,166,167],{},"X-Fibly-Verification"," header on every call to this connection (see\n",[34,170,172],{"href":171},"#verifying-the-caller-x-fibly-verification","Verifying the caller"," below). When off, Fibly\nnever attaches the header to calls on this connection.",[76,175,176],{},"Save. The password\u002Ftoken field is write-only: once saved, it's never shown again in the\ndashboard.",[16,178,179,180,183,184,186],{},"The first connection you create also generates your workspace's ",[20,181,182],{},"signing key"," automatically\n(see ",[34,185,172],{"href":171}," below).",[54,188,190],{"id":189},"building-a-tool","Building a tool",[16,192,193,194,197],{},"Under a connection, add one or more ",[20,195,196],{},"tools"," — each maps to a single HTTP call:",[73,199,200,206,314,321],{},[76,201,91,202,205],{},[20,203,204],{},"Add tool"," on the connection's page.",[76,207,97,208],{},[99,209,210,220,242,256,262,296,306],{},[76,211,212,215,216,219],{},[20,213,214],{},"Tool name"," — the identifier the agent uses internally to call this tool, e.g.\n",[114,217,218],{},"get_order_status",". Letters, numbers, and underscores only, and it must be unique across all\nyour connections.",[76,221,222,225,226,38,229,38,232,38,235,238,239,88],{},[20,223,224],{},"HTTP method"," — ",[114,227,228],{},"GET",[114,230,231],{},"POST",[114,233,234],{},"PUT",[114,236,237],{},"PATCH",", or ",[114,240,241],{},"DELETE",[76,243,244,247,248,251,252,255],{},[20,245,246],{},"Path"," — relative to the connection's base URL, e.g. ",[114,249,250],{},"\u002Forders\u002F{order_ref}",". Wrap a segment\nin ",[114,253,254],{},"{ }"," to reference a parameter (see below) — it's substituted with the argument value and\nURL-encoded before the call.",[76,257,258,261],{},[20,259,260],{},"Description"," — the prompt the AI model reads to decide when and how to call this tool.\nDescribe what it does and when it's useful; the model never sees this text rendered to the\ncustomer.",[76,263,264,267,268,271,272,275,276,38,279,38,282,38,285,238,288,291,292,295],{},[20,265,266],{},"Parameters"," — the arguments the model can fill in. Add each with a ",[20,269,270],{},"name",", a ",[20,273,274],{},"type","\n(",[114,277,278],{},"String",[114,280,281],{},"Number",[114,283,284],{},"Integer",[114,286,287],{},"Boolean",[114,289,290],{},"Enum"," with a fixed list of values), whether\nit's ",[20,293,294],{},"required",", and a description.",[76,297,298,301,302,88],{},[20,299,300],{},"Requires confirmation"," — see ",[34,303,305],{"href":304},"#confirmation-requires_confirmation","below",[76,307,308,301,311,88],{},[20,309,310],{},"Requires identity verification",[34,312,305],{"href":313},"#identity-verification-requires_verification",[76,315,316,317,320],{},"The tool editor has an ",[20,318,319],{},"English\u002FPolish"," language switch: the description and each\nparameter's description are localized, so the model reasons in whichever language the\nconversation is in. An English description is required; other languages fall back to it if\nleft blank.",[76,322,323,324,327],{},"Save, and toggle ",[20,325,326],{},"Enabled"," on when you're ready for the bot to use it live.",[54,329,331],{"id":330},"how-the-agent-calls-your-webhook","How the agent calls your webhook",[16,333,334],{},"When the model decides to use a tool, Fibly builds and sends the HTTP request for you:",[99,336,337,348,370,373,382],{},[76,338,339,340,343,344,347],{},"Any ",[114,341,342],{},"{name}"," placeholders in the ",[20,345,346],{},"path"," are filled from the matching argument (URL-encoded).",[76,349,350,351,354,355,357,358,360,361,354,364,357,366,357,368,88],{},"Whatever arguments are left over go to the ",[20,352,353],{},"query string"," for ",[114,356,228],{},"\u002F",[114,359,241],{},", or the ",[20,362,363],{},"JSON\nbody",[114,365,231],{},[114,367,234],{},[114,369,237],{},[76,371,372],{},"Your connection's authentication (Basic\u002FBearer) is applied automatically.",[76,374,375,376,378,379,381],{},"If the connection's ",[20,377,163],{}," setting is on, a signed ",[114,380,167],{},"\nheader is attached — see the next section. It's off by default, so unless you've turned it on,\nyour webhook won't receive this header at all.",[76,383,384,385,388,389,392],{},"The call has a fixed ",[20,386,387],{},"~10 second timeout",", and Fibly does ",[20,390,391],{},"not"," follow redirects — if your\nAPI 30x-redirects, update the connection's base URL instead of relying on a redirect.",[394,395,397],"h3",{"id":396},"what-the-agent-sees-back","What the agent sees back",[99,399,400,406],{},[76,401,67,402,405],{},[20,403,404],{},"2xx"," response with a JSON body is handed to the model as structured data it can use in its\nanswer. A 2xx with a non-JSON body is passed through as plain text.",[76,407,67,408,411],{},[20,409,410],{},"non-2xx"," response, a timeout, or a connection error is treated as a tool failure — the\nmodel is told the call didn't succeed and adapts its answer accordingly (it doesn't see your\nraw status code or body in that case).",[16,413,414],{},"Design your webhook's success responses to be small and directly useful — whatever you return in\na 2xx JSON body is what the model works with to answer the customer.",[54,416,418,419,421],{"id":417},"verifying-the-caller-x-fibly-verification","Verifying the caller (",[114,420,167],{},")",[16,423,424,425,427,428,431,432,434,435,438,439,442],{},"Turn on ",[20,426,163],{}," on a connection (off by default — see\n",[34,429,64],{"href":430},"#building-a-connection"," above) and every call on that connection carries a\nsigned JSON Web Token in the ",[114,433,167],{}," header. This is the part your developer needs\nto implement, and it matters even if your API also requires its own Basic\u002FBearer credentials: those\ncredentials only prove the request came from ",[49,436,437],{},"Fibly's backend somewhere","; the signed token proves\nit came from ",[49,440,441],{},"your tenant's"," configured connection, and carries the conversation and customer\ncontext you need to make authorization decisions. Once you turn the toggle on, Fibly sends the\nheader on every call to the connection — your server verifying it is a separate step, covered\nbelow; until you build that verification, the header just arrives unused.",[16,444,445,448,449,452],{},[20,446,447],{},"Why it exists."," Fibly cannot know your data model, so it cannot check, for example, that the\norder a customer is asking about actually belongs to them. What it ",[49,450,451],{},"can"," do is hand you a\ncryptographically signed, tamper-proof statement of who the visitor is (as far as Fibly's own\nverification goes) and what conversation the call belongs to — so your server can perform that\nownership check itself.",[394,454,456],{"id":455},"the-jwks-endpoint","The JWKS endpoint",[16,458,459,460,463,464,467],{},"Fibly generates an RSA-2048 signing keypair for your workspace the first time you create a\nconnection. The private key never leaves Fibly; the public key is published at your workspace's\n",[20,461,462],{},"JWKS"," endpoint — a public, unauthenticated URL that returns your active (and any recently\nrotated) public keys in the standard ",[114,465,466],{},"{\"keys\": [...]}"," JWKS format.",[16,469,470,473,474,477,478,481,482,485,486,489,490,493,494,497,498,501,502,509],{},[20,471,472],{},"Copy your JWKS URL from the dashboard — don't try to build it yourself."," It's fixed for your\nworkspace, shown alongside the ",[20,475,476],{},"Key ID"," and ",[20,479,480],{},"Connection ID"," on the connection's ",[20,483,484],{},"Signing\nkeys"," panel, next to a ",[20,487,488],{},"Download public key (PEM)"," button and a ",[20,491,492],{},"Rotate key"," action. Copy the\nJWKS URL into the ",[114,495,496],{},"JWKS_URL"," constant and the Connection ID into the ",[114,499,500],{},"CONNECTION_ID"," constant in\nthe code samples below — both belong in your server's hardcoded configuration, not something you\ncompute from a request. Rotating immediately starts signing new tokens with a new key, but the\nprevious key stays published in the JWKS for a short overlap window so any tokens already in\nflight still verify — ",[20,503,504,505,508],{},"fetch keys by ",[114,506,507],{},"kid"," and don't hardcode a single key's contents",", or\nrotation will break your verification.",[394,511,513],{"id":512},"claims-reference","Claims reference",[16,515,516,517,520,521,523],{},"The token is a compact JWS, algorithm ",[20,518,519],{},"RS256",", with the signing key's id in the JWT header as\n",[114,522,507],{},". Its payload:",[525,526,527,546],"table",{},[528,529,530],"thead",{},[531,532,533,537,540,543],"tr",{},[534,535,536],"th",{},"Claim",[534,538,539],{},"Type",[534,541,542],{},"Always present?",[534,544,545],{},"Meaning",[547,548,549,569,587,605,620,637,658,673,701,722,736,750,781,803,825,840,856,875],"tbody",{},[531,550,551,557,560,563],{},[552,553,554],"td",{},[114,555,556],{},"iss",[552,558,559],{},"string",[552,561,562],{},"always",[552,564,565,566,88],{},"Always the literal string ",[114,567,568],{},"\"fibly\"",[531,570,571,576,579,581],{},[552,572,573],{},[114,574,575],{},"aud",[552,577,578],{},"string (UUID)",[552,580,562],{},[552,582,583,584,586],{},"The id of the ",[20,585,70],{}," the call belongs to.",[531,588,589,594,596,598],{},[552,590,591],{},[114,592,593],{},"tenant_id",[552,595,578],{},[552,597,562],{},[552,599,600,601,604],{},"Your workspace id. Useful as an optional extra check that a token was signed for your workspace — don't use it to build the JWKS URL; that URL is fixed, copy it from the dashboard (see ",[34,602,456],{"href":603},"#the-jwks-endpoint"," above).",[531,606,607,612,615,617],{},[552,608,609],{},[114,610,611],{},"iat",[552,613,614],{},"number (Unix timestamp)",[552,616,562],{},[552,618,619],{},"When the token was issued.",[531,621,622,627,629,631],{},[552,623,624],{},[114,625,626],{},"nbf",[552,628,614],{},[552,630,562],{},[552,632,633,634,636],{},"Same as ",[114,635,611],{}," — the token isn't valid before this time.",[531,638,639,644,646,648],{},[552,640,641],{},[114,642,643],{},"exp",[552,645,614],{},[552,647,562],{},[552,649,650,651,654,655,657],{},"Expiry, about ",[20,652,653],{},"120 seconds"," after ",[114,656,611],{},". The token is meant to authenticate this one call, not to be replayed later.",[531,659,660,665,668,670],{},[552,661,662],{},[114,663,664],{},"jti",[552,666,667],{},"string (hex)",[552,669,562],{},[552,671,672],{},"A unique token id, useful if you want to reject an exact replay.",[531,674,675,680,686,691],{},[552,676,677],{},[114,678,679],{},"conversation_id",[552,681,682,683],{},"string (UUID) or ",[114,684,685],{},"null",[552,687,688,689,421],{},"always (value may be ",[114,690,685],{},[552,692,693,694,696,697,700],{},"The conversation thread this call happened in. ",[114,695,685],{}," for calls made outside a real conversation, such as the dashboard's ",[20,698,699],{},"Test"," action.",[531,702,703,708,710,712],{},[552,704,705],{},[114,706,707],{},"language",[552,709,559],{},[552,711,562],{},[552,713,714,715,718,719,88],{},"The bot's active language for this call, e.g. ",[114,716,717],{},"en"," or ",[114,720,721],{},"pl",[531,723,724,729,731,733],{},[552,725,726],{},[114,727,728],{},"tool_name",[552,730,559],{},[552,732,562],{},[552,734,735],{},"The name of the tool that was called.",[531,737,738,743,745,747],{},[552,739,740],{},[114,741,742],{},"tool_call_id",[552,744,667],{},[552,746,562],{},[552,748,749],{},"A unique id for this specific call, useful for correlating with your own logs.",[531,751,752,757,759,762],{},[552,753,754],{},[114,755,756],{},"channel",[552,758,559],{},[552,760,761],{},"only inside a real conversation",[552,763,764,765,768,769,38,772,38,775,238,778,88],{},"The channel the conversation is on: ",[114,766,767],{},"chat"," (the website widget), ",[114,770,771],{},"email",[114,773,774],{},"whatsapp",[114,776,777],{},"facebook",[114,779,780],{},"instagram",[531,782,783,788,791,793],{},[552,784,785],{},[114,786,787],{},"email_verified",[552,789,790],{},"boolean",[552,792,761],{},[552,794,795,796,799,800,802],{},"Whether the visitor's email is trusted overall: they completed Fibly's own verification (magic link, including a returning visitor recognized via their browser's saved verification for up to 90 days), ",[20,797,798],{},"or"," the conversation came in over the email channel (an email sender is inherently a stronger identity signal). This is the single flag the ",[20,801,310],{}," tool gate itself checks — see below.",[531,804,805,810,813,815],{},[552,806,807],{},[114,808,809],{},"channel_data",[552,811,812],{},"object",[552,814,761],{},[552,816,817,818,301,820,824],{},"Per-channel context, nested one level under the key matching ",[114,819,756],{},[34,821,823],{"href":822},"#channel-data-channel_data","Channel data"," below.",[531,826,827,832,834,837],{},[552,828,829],{},[114,830,831],{},"customer_id",[552,833,578],{},[552,835,836],{},"only when the conversation has an identified customer",[552,838,839],{},"Fibly's internal id for this customer.",[531,841,842,846,851,853],{},[552,843,844],{},[114,845,270],{},[552,847,848,849],{},"string or ",[114,850,685],{},[552,852,836],{},[552,854,855],{},"The customer's name, if known.",[531,857,858,862,866,868],{},[552,859,860],{},[114,861,771],{},[552,863,848,864],{},[114,865,685],{},[552,867,836],{},[552,869,870,871,874],{},"The customer's email on file. ",[20,872,873],{},"This is not the same as verified"," — see the warning below.",[531,876,877,882,886,888],{},[552,878,879],{},[114,880,881],{},"phone",[552,883,848,884],{},[114,885,685],{},[552,887,836],{},[552,889,890],{},"The customer's phone number, if known.",[16,892,893],{},"The message transcript is never included, only conversation\u002Fcustomer metadata.",[394,895,897,898,421],{"id":896},"channel-data-channel_data","Channel data (",[114,899,809],{},[16,901,902,904,905,911,912,915],{},[114,903,809],{}," carries context specific to the channel the conversation happened on. ",[20,906,907,908,910],{},"Only the\none key matching the ",[114,909,756],{}," claim is present"," — read it as\n",[114,913,914],{},"claims[\"channel_data\"][claims[\"channel\"]]"," rather than checking every channel's shape:",[525,917,918,934],{},[528,919,920],{},[531,921,922,926,931],{},[534,923,924],{},[114,925,756],{},[534,927,928,929],{},"key in ",[114,930,809],{},[534,932,933],{},"fields",[547,935,936,977,991,1005,1021],{},[531,937,938,943,947],{},[552,939,940,942],{},[114,941,767],{}," (website widget)",[552,944,945],{},[114,946,767],{},[552,948,949,38,952,38,955,38,958,38,961,38,964,967,968,357,971,357,974],{},[114,950,951],{},"ip",[114,953,954],{},"user_agent",[114,956,957],{},"browser_name",[114,959,960],{},"browser_version",[114,962,963],{},"os_name",[114,965,966],{},"os_version",", and geo ",[114,969,970],{},"country_code",[114,972,973],{},"country",[114,975,976],{},"city",[531,978,979,983,987],{},[552,980,981],{},[114,982,771],{},[552,984,985],{},[114,986,771],{},[552,988,989],{},[114,990,771],{},[531,992,993,997,1001],{},[552,994,995],{},[114,996,774],{},[552,998,999],{},[114,1000,774],{},[552,1002,1003],{},[114,1004,881],{},[531,1006,1007,1011,1015],{},[552,1008,1009],{},[114,1010,777],{},[552,1012,1013],{},[114,1014,777],{},[552,1016,1017,1020],{},[114,1018,1019],{},"psid"," (the Messenger page-scoped user id)",[531,1022,1023,1027,1031],{},[552,1024,1025],{},[114,1026,780],{},[552,1028,1029],{},[114,1030,780],{},[552,1032,1033,1036],{},[114,1034,1035],{},"igsid"," (the Instagram-scoped user id)",[16,1038,1039,1040,1042,1043,1045],{},"Only the ",[114,1041,767],{}," channel carries IP address, user agent, browser\u002FOS, and geolocation — that data\ncomes from the visitor's browser session on your website, which doesn't exist for the other\nchannels. Any field Fibly doesn't have for the call is ",[114,1044,685],{}," rather than omitted, so the shape for\na given channel is always the same.",[1047,1048,1049],"blockquote",{},[16,1050,1051,1056],{},[20,1052,1053,1055],{},[114,1054,809],{}," is personal data."," IP address, browser\u002FOS fingerprint, email, phone number, and\nthe Messenger\u002FInstagram scoped ids are all personal data under most privacy regimes. Handle,\nstore, and log it accordingly, and don't forward it anywhere you wouldn't forward the rest of the\ncustomer's contact details.",[1047,1058,1059],{},[16,1060,1061,1067,1068,1070,1071,1073,1074,1077,1078,1081,1082,1073,1084,1086],{},[20,1062,1063,1064,1066],{},"The ",[114,1065,771],{}," claim is not automatically verified."," It reflects whatever email Fibly has on\nfile for the customer, whether or not it's been proven. Before you trust an ",[114,1069,771],{}," claim for\nanything sensitive, check ",[114,1072,787],{}," is ",[114,1075,1076],{},"true",". If your tool is marked ",[20,1079,1080],{},"Requires identity\nverification"," in the dashboard, Fibly guarantees ",[114,1083,787],{},[114,1085,1076],{}," by the time your\nwebhook is ever called for that tool — see the next section.",[394,1088,1090],{"id":1089},"verifying-the-token-step-by-step","Verifying the token, step by step",[73,1092,1093,1099,1109,1112,1136,1139],{},[76,1094,1095,1096,1098],{},"Read the ",[114,1097,167],{}," header from the incoming request. If it's missing, treat the\ncall as unauthenticated — decide your own policy (reject it, or only serve non-sensitive data).",[76,1100,1101,1102,1105,1106,1108],{},"Fetch (and cache) your ",[20,1103,1104],{},"fixed"," JWKS URL — the one you copied from the Signing keys panel, not\none you compute — and, from the token's header, pick the key whose ",[114,1107,507],{}," matches.",[76,1110,1111],{},"Verify the token's RS256 signature against that key.",[76,1113,1114,1115,1117,1118,1120,1121,1123,1124,1126,1127,1129,1130,1132,1133,1135],{},"Validate ",[114,1116,643],{}," (not expired), ",[114,1119,556],{}," (equals ",[114,1122,568],{},"), and ",[114,1125,575],{}," (equals this connection's\n",[20,1128,480],{},", also copied from the Signing keys panel). Always check ",[114,1131,575],{},", not just when\nyou want to \"pin\" verification — it's what stops a token signed for a different connection from\nvalidating here. Optionally also check the ",[114,1134,593],{}," claim equals your workspace id, as extra\ndefense.",[76,1137,1138],{},"Only once the signature and standard claims check out, trust the rest of the payload.",[76,1140,1141,1142,1145],{},"Perform your ",[20,1143,1144],{},"own"," resource-ownership check using the verified claims — Fibly does not know\nyour data model and cannot do this for you.",[16,1147,1148,1149,1151],{},"Because a rotated key stays valid for a short overlap window, your JWKS client should cache\nresponses briefly and refetch on a ",[114,1150,507],{}," it doesn't recognize, rather than caching a single key\nforever.",[16,1153,1154,1157,1158,1160,1161,1164],{},[20,1155,1156],{},"Security note: never let the token tell you which keys to verify it with."," Reading a ",[114,1159,593],{},"\n(or any other claim) out of an ",[49,1162,1163],{},"unverified"," token and using it to build the JWKS URL you then fetch\nis unsafe — a token signed with a different Fibly tenant's key would fetch and validate against\nthat tenant's JWKS, letting anyone who can obtain a signed token from their own (attacker-owned)\nworkspace impersonate calls to your webhook. Always verify against the one fixed JWKS URL you\ncopied from your connection's Signing keys panel, never a URL built from the token itself.",[394,1166,1168],{"id":1167},"worked-example-an-order-status-tool","Worked example: an order-status tool",[16,1170,1171,1172,1174,1175,1178,1179,1182,1183,1185,1186,1188,1189,1191],{},"Say you build a ",[114,1173,218],{}," tool: ",[114,1176,1177],{},"GET \u002Forders\u002F{order_ref}"," on your Orders API connection,\nwith one required ",[114,1180,1181],{},"order_ref"," string parameter and ",[20,1184,310],{}," turned on.\nThe connection also has ",[20,1187,163],{}," turned on — without it, no\n",[114,1190,167],{}," header would arrive at all, and steps 3-4 below wouldn't be possible.",[73,1193,1194,1209,1222,1237],{},[76,1195,1196,1197,1200,1201,1204,1205,1208],{},"A customer asks \"where's my order 12345?\" The model has ",[114,1198,1199],{},"order_ref=\"12345\""," but the visitor\nhasn't verified their email yet on this conversation. Because the tool requires verification,\n",[20,1202,1203],{},"Fibly never calls your webhook"," — it returns a ",[114,1206,1207],{},"verification_required"," status to the model,\nwhich asks the customer to verify their email (Fibly's own magic-link flow) and retries once\nthey do.",[76,1210,1211,1212],{},"Once verified, Fibly calls:\n",[1213,1214,1219],"pre",{"className":1215,"code":1217,"language":1218},[1216],"language-text","GET https:\u002F\u002Fapi.example.com\u002Forders\u002F12345\nAuthorization: Bearer \u003Cyour connection's token>\nX-Fibly-Verification: \u003Cjwt>\n","text",[114,1220,1217],{"__ignoreMap":1221},"",[76,1223,1224,1225,1227,1228,1230,1231,1233,1234,88],{},"Your server verifies the JWT as above. Because this tool requires verification, ",[114,1226,787],{},"\nis guaranteed ",[114,1229,1076],{},", and ",[114,1232,771],{}," is the visitor's verified address, e.g. ",[114,1235,1236],{},"jane@example.com",[76,1238,1239,1240,1243,1244,1247,1248,1250,1251,1291,1292,1295],{},"Your server looks up order ",[114,1241,1242],{},"12345"," in your own database and compares ",[20,1245,1246],{},"its"," owner's email to\nthe token's verified ",[114,1249,771],{},". If they match, return the status:\n",[1213,1252,1256],{"className":1253,"code":1254,"language":1255,"meta":1221,"style":1221},"language-json shiki shiki-themes github-dark","{ \"order_ref\": \"12345\", \"status\": \"shipped\" }\n","json",[114,1257,1258],{"__ignoreMap":1221},[1259,1260,1263,1267,1271,1274,1278,1280,1283,1285,1288],"span",{"class":1261,"line":1262},"line",1,[1259,1264,1266],{"class":1265},"s95oV","{ ",[1259,1268,1270],{"class":1269},"sDLfK","\"order_ref\"",[1259,1272,1273],{"class":1265},": ",[1259,1275,1277],{"class":1276},"sU2Wk","\"12345\"",[1259,1279,38],{"class":1265},[1259,1281,1282],{"class":1269},"\"status\"",[1259,1284,1273],{"class":1265},[1259,1286,1287],{"class":1276},"\"shipped\"",[1259,1289,1290],{"class":1265}," }\n","\nIf they don't match — or the order doesn't exist — return a ",[20,1293,1294],{},"404",", not a 403: a 403 would\nconfirm to an attacker that order 12345 exists at all.",[54,1297,1299],{"id":1298},"code-samples-verifying-the-token","Code samples: verifying the token",[16,1301,1302,1303,1305,1306,357,1308,357,1310,1312,1313,477,1315,1317],{},"Each sample below reads the header, fetches the JWKS (caching it), verifies the RS256 signature\nby ",[114,1304,507],{},", checks ",[114,1307,643],{},[114,1309,556],{},[114,1311,575],{},", and performs the same email-ownership check as the worked\nexample. Replace ",[114,1314,496],{},[114,1316,500],{}," with the values shown in this connection's Signing\nkeys panel — both are hardcoded configuration, never derived from the token (see the security note\nabove).",[1319,1320,1322,1906,2459],"code-tabs",{":captions":1321},"{\"python\":\"PyJWT\",\"node\":\"jose\",\"php\":\"firebase\u002Fphp-jwt\"}",[1323,1324,1325],"template",{"v-slot:python":1221},[1213,1326,1330],{"className":1327,"code":1328,"language":1329,"meta":1221,"style":1221},"language-python shiki shiki-themes github-dark","import jwt\nfrom jwt import PyJWKClient\n\n# Copy both values from this connection's Signing keys panel and hardcode them as\n# configuration — never derive JWKS_URL from a claim inside the (unverified) token\n# itself, or a token signed by a different tenant's key could validate here.\nJWKS_URL = \"https:\u002F\u002Fapp.fibly.io\u002Fapi\u002Ftool-integrations\u002Fcustom-webhooks\u002F\u003Cyour-workspace-id>\u002Fjwks.json\"  # copy from the dashboard\nCONNECTION_ID = \"c1a2e3f4-5678-4abc-9def-0123456789ab\"  # \"Connection ID\" in the Signing keys panel\n\n# PyJWKClient caches the JWKS response and refetches when it meets an unknown `kid`.\n_jwks_client = PyJWKClient(JWKS_URL, cache_keys=True)\n\ndef verify_fibly_token(token: str) -> dict:\n    signing_key = _jwks_client.get_signing_key_from_jwt(token)\n    claims = jwt.decode(\n        token,\n        signing_key.key,\n        algorithms=[\"RS256\"],\n        audience=CONNECTION_ID,\n        issuer=\"fibly\",\n        options={\"require\": [\"exp\", \"iat\", \"iss\", \"aud\"]},\n    )\n    return claims\n\n\ndef handle_get_order_status(order_ref: str, headers: dict) -> tuple[dict, int]:\n    token = headers.get(\"X-Fibly-Verification\", \"\")\n    if not token:\n        return {\"error\": \"missing verification token\"}, 401\n    try:\n        claims = verify_fibly_token(token)\n    except Exception:\n        return {\"error\": \"invalid verification token\"}, 401\n\n    order = get_order(order_ref)  # your own lookup\n    if order is None:\n        return {\"error\": \"not found\"}, 404\n\n    verified_email = (claims.get(\"email\") or \"\").lower()\n    if not claims.get(\"email_verified\") or order.customer_email.lower() != verified_email:\n        return {\"error\": \"not found\"}, 404  # 404, not 403 — don't confirm the order exists\n\n    return {\"order_ref\": order_ref, \"status\": order.status}, 200\n","python",[114,1331,1332,1341,1355,1362,1369,1375,1381,1395,1408,1413,1419,1447,1452,1477,1488,1499,1505,1511,1528,1541,1553,1591,1597,1606,1611,1616,1647,1668,1680,1703,1711,1722,1733,1751,1756,1770,1786,1805,1810,1835,1861,1881,1886],{"__ignoreMap":1221},[1259,1333,1334,1338],{"class":1261,"line":1262},[1259,1335,1337],{"class":1336},"snl16","import",[1259,1339,1340],{"class":1265}," jwt\n",[1259,1342,1344,1347,1350,1352],{"class":1261,"line":1343},2,[1259,1345,1346],{"class":1336},"from",[1259,1348,1349],{"class":1265}," jwt ",[1259,1351,1337],{"class":1336},[1259,1353,1354],{"class":1265}," PyJWKClient\n",[1259,1356,1358],{"class":1261,"line":1357},3,[1259,1359,1361],{"emptyLinePlaceholder":1360},true,"\n",[1259,1363,1365],{"class":1261,"line":1364},4,[1259,1366,1368],{"class":1367},"sAwPA","# Copy both values from this connection's Signing keys panel and hardcode them as\n",[1259,1370,1372],{"class":1261,"line":1371},5,[1259,1373,1374],{"class":1367},"# configuration — never derive JWKS_URL from a claim inside the (unverified) token\n",[1259,1376,1378],{"class":1261,"line":1377},6,[1259,1379,1380],{"class":1367},"# itself, or a token signed by a different tenant's key could validate here.\n",[1259,1382,1384,1386,1389,1392],{"class":1261,"line":1383},7,[1259,1385,496],{"class":1269},[1259,1387,1388],{"class":1336}," =",[1259,1390,1391],{"class":1276}," \"https:\u002F\u002Fapp.fibly.io\u002Fapi\u002Ftool-integrations\u002Fcustom-webhooks\u002F\u003Cyour-workspace-id>\u002Fjwks.json\"",[1259,1393,1394],{"class":1367},"  # copy from the dashboard\n",[1259,1396,1398,1400,1402,1405],{"class":1261,"line":1397},8,[1259,1399,500],{"class":1269},[1259,1401,1388],{"class":1336},[1259,1403,1404],{"class":1276}," \"c1a2e3f4-5678-4abc-9def-0123456789ab\"",[1259,1406,1407],{"class":1367},"  # \"Connection ID\" in the Signing keys panel\n",[1259,1409,1411],{"class":1261,"line":1410},9,[1259,1412,1361],{"emptyLinePlaceholder":1360},[1259,1414,1416],{"class":1261,"line":1415},10,[1259,1417,1418],{"class":1367},"# PyJWKClient caches the JWKS response and refetches when it meets an unknown `kid`.\n",[1259,1420,1422,1425,1428,1431,1433,1435,1439,1441,1444],{"class":1261,"line":1421},11,[1259,1423,1424],{"class":1265},"_jwks_client ",[1259,1426,1427],{"class":1336},"=",[1259,1429,1430],{"class":1265}," PyJWKClient(",[1259,1432,496],{"class":1269},[1259,1434,38],{"class":1265},[1259,1436,1438],{"class":1437},"s9osk","cache_keys",[1259,1440,1427],{"class":1336},[1259,1442,1443],{"class":1269},"True",[1259,1445,1446],{"class":1265},")\n",[1259,1448,1450],{"class":1261,"line":1449},12,[1259,1451,1361],{"emptyLinePlaceholder":1360},[1259,1453,1455,1458,1462,1465,1468,1471,1474],{"class":1261,"line":1454},13,[1259,1456,1457],{"class":1336},"def",[1259,1459,1461],{"class":1460},"svObZ"," verify_fibly_token",[1259,1463,1464],{"class":1265},"(token: ",[1259,1466,1467],{"class":1269},"str",[1259,1469,1470],{"class":1265},") -> ",[1259,1472,1473],{"class":1269},"dict",[1259,1475,1476],{"class":1265},":\n",[1259,1478,1480,1483,1485],{"class":1261,"line":1479},14,[1259,1481,1482],{"class":1265},"    signing_key ",[1259,1484,1427],{"class":1336},[1259,1486,1487],{"class":1265}," _jwks_client.get_signing_key_from_jwt(token)\n",[1259,1489,1491,1494,1496],{"class":1261,"line":1490},15,[1259,1492,1493],{"class":1265},"    claims ",[1259,1495,1427],{"class":1336},[1259,1497,1498],{"class":1265}," jwt.decode(\n",[1259,1500,1502],{"class":1261,"line":1501},16,[1259,1503,1504],{"class":1265},"        token,\n",[1259,1506,1508],{"class":1261,"line":1507},17,[1259,1509,1510],{"class":1265},"        signing_key.key,\n",[1259,1512,1514,1517,1519,1522,1525],{"class":1261,"line":1513},18,[1259,1515,1516],{"class":1437},"        algorithms",[1259,1518,1427],{"class":1336},[1259,1520,1521],{"class":1265},"[",[1259,1523,1524],{"class":1276},"\"RS256\"",[1259,1526,1527],{"class":1265},"],\n",[1259,1529,1531,1534,1536,1538],{"class":1261,"line":1530},19,[1259,1532,1533],{"class":1437},"        audience",[1259,1535,1427],{"class":1336},[1259,1537,500],{"class":1269},[1259,1539,1540],{"class":1265},",\n",[1259,1542,1544,1547,1549,1551],{"class":1261,"line":1543},20,[1259,1545,1546],{"class":1437},"        issuer",[1259,1548,1427],{"class":1336},[1259,1550,568],{"class":1276},[1259,1552,1540],{"class":1265},[1259,1554,1556,1559,1561,1564,1567,1570,1573,1575,1578,1580,1583,1585,1588],{"class":1261,"line":1555},21,[1259,1557,1558],{"class":1437},"        options",[1259,1560,1427],{"class":1336},[1259,1562,1563],{"class":1265},"{",[1259,1565,1566],{"class":1276},"\"require\"",[1259,1568,1569],{"class":1265},": [",[1259,1571,1572],{"class":1276},"\"exp\"",[1259,1574,38],{"class":1265},[1259,1576,1577],{"class":1276},"\"iat\"",[1259,1579,38],{"class":1265},[1259,1581,1582],{"class":1276},"\"iss\"",[1259,1584,38],{"class":1265},[1259,1586,1587],{"class":1276},"\"aud\"",[1259,1589,1590],{"class":1265},"]},\n",[1259,1592,1594],{"class":1261,"line":1593},22,[1259,1595,1596],{"class":1265},"    )\n",[1259,1598,1600,1603],{"class":1261,"line":1599},23,[1259,1601,1602],{"class":1336},"    return",[1259,1604,1605],{"class":1265}," claims\n",[1259,1607,1609],{"class":1261,"line":1608},24,[1259,1610,1361],{"emptyLinePlaceholder":1360},[1259,1612,1614],{"class":1261,"line":1613},25,[1259,1615,1361],{"emptyLinePlaceholder":1360},[1259,1617,1619,1621,1624,1627,1629,1632,1634,1637,1639,1641,1644],{"class":1261,"line":1618},26,[1259,1620,1457],{"class":1336},[1259,1622,1623],{"class":1460}," handle_get_order_status",[1259,1625,1626],{"class":1265},"(order_ref: ",[1259,1628,1467],{"class":1269},[1259,1630,1631],{"class":1265},", headers: ",[1259,1633,1473],{"class":1269},[1259,1635,1636],{"class":1265},") -> tuple[",[1259,1638,1473],{"class":1269},[1259,1640,38],{"class":1265},[1259,1642,1643],{"class":1269},"int",[1259,1645,1646],{"class":1265},"]:\n",[1259,1648,1650,1653,1655,1658,1661,1663,1666],{"class":1261,"line":1649},27,[1259,1651,1652],{"class":1265},"    token ",[1259,1654,1427],{"class":1336},[1259,1656,1657],{"class":1265}," headers.get(",[1259,1659,1660],{"class":1276},"\"X-Fibly-Verification\"",[1259,1662,38],{"class":1265},[1259,1664,1665],{"class":1276},"\"\"",[1259,1667,1446],{"class":1265},[1259,1669,1671,1674,1677],{"class":1261,"line":1670},28,[1259,1672,1673],{"class":1336},"    if",[1259,1675,1676],{"class":1336}," not",[1259,1678,1679],{"class":1265}," token:\n",[1259,1681,1683,1686,1689,1692,1694,1697,1700],{"class":1261,"line":1682},29,[1259,1684,1685],{"class":1336},"        return",[1259,1687,1688],{"class":1265}," {",[1259,1690,1691],{"class":1276},"\"error\"",[1259,1693,1273],{"class":1265},[1259,1695,1696],{"class":1276},"\"missing verification token\"",[1259,1698,1699],{"class":1265},"}, ",[1259,1701,1702],{"class":1269},"401\n",[1259,1704,1706,1709],{"class":1261,"line":1705},30,[1259,1707,1708],{"class":1336},"    try",[1259,1710,1476],{"class":1265},[1259,1712,1714,1717,1719],{"class":1261,"line":1713},31,[1259,1715,1716],{"class":1265},"        claims ",[1259,1718,1427],{"class":1336},[1259,1720,1721],{"class":1265}," verify_fibly_token(token)\n",[1259,1723,1725,1728,1731],{"class":1261,"line":1724},32,[1259,1726,1727],{"class":1336},"    except",[1259,1729,1730],{"class":1269}," Exception",[1259,1732,1476],{"class":1265},[1259,1734,1736,1738,1740,1742,1744,1747,1749],{"class":1261,"line":1735},33,[1259,1737,1685],{"class":1336},[1259,1739,1688],{"class":1265},[1259,1741,1691],{"class":1276},[1259,1743,1273],{"class":1265},[1259,1745,1746],{"class":1276},"\"invalid verification token\"",[1259,1748,1699],{"class":1265},[1259,1750,1702],{"class":1269},[1259,1752,1754],{"class":1261,"line":1753},34,[1259,1755,1361],{"emptyLinePlaceholder":1360},[1259,1757,1759,1762,1764,1767],{"class":1261,"line":1758},35,[1259,1760,1761],{"class":1265},"    order ",[1259,1763,1427],{"class":1336},[1259,1765,1766],{"class":1265}," get_order(order_ref)  ",[1259,1768,1769],{"class":1367},"# your own lookup\n",[1259,1771,1773,1775,1778,1781,1784],{"class":1261,"line":1772},36,[1259,1774,1673],{"class":1336},[1259,1776,1777],{"class":1265}," order ",[1259,1779,1780],{"class":1336},"is",[1259,1782,1783],{"class":1269}," None",[1259,1785,1476],{"class":1265},[1259,1787,1789,1791,1793,1795,1797,1800,1802],{"class":1261,"line":1788},37,[1259,1790,1685],{"class":1336},[1259,1792,1688],{"class":1265},[1259,1794,1691],{"class":1276},[1259,1796,1273],{"class":1265},[1259,1798,1799],{"class":1276},"\"not found\"",[1259,1801,1699],{"class":1265},[1259,1803,1804],{"class":1269},"404\n",[1259,1806,1808],{"class":1261,"line":1807},38,[1259,1809,1361],{"emptyLinePlaceholder":1360},[1259,1811,1813,1816,1818,1821,1824,1827,1829,1832],{"class":1261,"line":1812},39,[1259,1814,1815],{"class":1265},"    verified_email ",[1259,1817,1427],{"class":1336},[1259,1819,1820],{"class":1265}," (claims.get(",[1259,1822,1823],{"class":1276},"\"email\"",[1259,1825,1826],{"class":1265},") ",[1259,1828,798],{"class":1336},[1259,1830,1831],{"class":1276}," \"\"",[1259,1833,1834],{"class":1265},").lower()\n",[1259,1836,1838,1840,1842,1845,1848,1850,1852,1855,1858],{"class":1261,"line":1837},40,[1259,1839,1673],{"class":1336},[1259,1841,1676],{"class":1336},[1259,1843,1844],{"class":1265}," claims.get(",[1259,1846,1847],{"class":1276},"\"email_verified\"",[1259,1849,1826],{"class":1265},[1259,1851,798],{"class":1336},[1259,1853,1854],{"class":1265}," order.customer_email.lower() ",[1259,1856,1857],{"class":1336},"!=",[1259,1859,1860],{"class":1265}," verified_email:\n",[1259,1862,1864,1866,1868,1870,1872,1874,1876,1878],{"class":1261,"line":1863},41,[1259,1865,1685],{"class":1336},[1259,1867,1688],{"class":1265},[1259,1869,1691],{"class":1276},[1259,1871,1273],{"class":1265},[1259,1873,1799],{"class":1276},[1259,1875,1699],{"class":1265},[1259,1877,1294],{"class":1269},[1259,1879,1880],{"class":1367},"  # 404, not 403 — don't confirm the order exists\n",[1259,1882,1884],{"class":1261,"line":1883},42,[1259,1885,1361],{"emptyLinePlaceholder":1360},[1259,1887,1889,1891,1893,1895,1898,1900,1903],{"class":1261,"line":1888},43,[1259,1890,1602],{"class":1336},[1259,1892,1688],{"class":1265},[1259,1894,1270],{"class":1276},[1259,1896,1897],{"class":1265},": order_ref, ",[1259,1899,1282],{"class":1276},[1259,1901,1902],{"class":1265},": order.status}, ",[1259,1904,1905],{"class":1269},"200\n",[1323,1907,1908],{"v-slot:node":1221},[1213,1909,1913],{"className":1910,"code":1911,"language":1912,"meta":1221,"style":1221},"language-js shiki shiki-themes github-dark","import { createRemoteJWKSet, jwtVerify } from 'jose';\n\n\u002F\u002F Copy both values from this connection's Signing keys panel and hardcode them as\n\u002F\u002F configuration — never derive JWKS_URL from a claim inside the (unverified) token\n\u002F\u002F itself, or a token signed by a different tenant's key could validate here.\nconst JWKS_URL = new URL(\n  'https:\u002F\u002Fapp.fibly.io\u002Fapi\u002Ftool-integrations\u002Fcustom-webhooks\u002F\u003Cyour-workspace-id>\u002Fjwks.json', \u002F\u002F copy from the dashboard\n);\nconst CONNECTION_ID = 'c1a2e3f4-5678-4abc-9def-0123456789ab'; \u002F\u002F \"Connection ID\" in the Signing keys panel\n\n\u002F\u002F createRemoteJWKSet caches the response and refetches on an unrecognized `kid`.\nconst jwks = createRemoteJWKSet(JWKS_URL);\n\nasync function verifyFiblyToken(token) {\n  const { payload } = await jwtVerify(token, jwks, {\n    issuer: 'fibly',\n    audience: CONNECTION_ID,\n    algorithms: ['RS256'],\n  });\n  return payload;\n}\n\napp.get('\u002Forders\u002F:ref', async (req, res) => {\n  const token = req.header('X-Fibly-Verification');\n  if (!token) return res.status(401).json({ error: 'missing verification token' });\n\n  let claims;\n  try {\n    claims = await verifyFiblyToken(token);\n  } catch {\n    return res.status(401).json({ error: 'invalid verification token' });\n  }\n\n  const order = await getOrder(req.params.ref); \u002F\u002F your own lookup\n  if (!order) return res.status(404).json({ error: 'not found' });\n\n  const verifiedEmail = (claims.email || '').toLowerCase();\n  if (!claims.email_verified || order.customerEmail.toLowerCase() !== verifiedEmail) {\n    return res.status(404).json({ error: 'not found' }); \u002F\u002F 404, not 403\n  }\n\n  res.json({ order_ref: req.params.ref, status: order.status });\n});\n","js",[114,1914,1915,1930,1934,1939,1944,1949,1968,1978,1983,2001,2005,2010,2029,2033,2052,2077,2087,2096,2106,2111,2119,2124,2128,2164,2186,2227,2231,2239,2246,2259,2269,2292,2297,2301,2321,2353,2357,2383,2410,2436,2440,2444,2454],{"__ignoreMap":1221},[1259,1916,1917,1919,1922,1924,1927],{"class":1261,"line":1262},[1259,1918,1337],{"class":1336},[1259,1920,1921],{"class":1265}," { createRemoteJWKSet, jwtVerify } ",[1259,1923,1346],{"class":1336},[1259,1925,1926],{"class":1276}," 'jose'",[1259,1928,1929],{"class":1265},";\n",[1259,1931,1932],{"class":1261,"line":1343},[1259,1933,1361],{"emptyLinePlaceholder":1360},[1259,1935,1936],{"class":1261,"line":1357},[1259,1937,1938],{"class":1367},"\u002F\u002F Copy both values from this connection's Signing keys panel and hardcode them as\n",[1259,1940,1941],{"class":1261,"line":1364},[1259,1942,1943],{"class":1367},"\u002F\u002F configuration — never derive JWKS_URL from a claim inside the (unverified) token\n",[1259,1945,1946],{"class":1261,"line":1371},[1259,1947,1948],{"class":1367},"\u002F\u002F itself, or a token signed by a different tenant's key could validate here.\n",[1259,1950,1951,1954,1957,1959,1962,1965],{"class":1261,"line":1377},[1259,1952,1953],{"class":1336},"const",[1259,1955,1956],{"class":1269}," JWKS_URL",[1259,1958,1388],{"class":1336},[1259,1960,1961],{"class":1336}," new",[1259,1963,1964],{"class":1460}," URL",[1259,1966,1967],{"class":1265},"(\n",[1259,1969,1970,1973,1975],{"class":1261,"line":1383},[1259,1971,1972],{"class":1276},"  'https:\u002F\u002Fapp.fibly.io\u002Fapi\u002Ftool-integrations\u002Fcustom-webhooks\u002F\u003Cyour-workspace-id>\u002Fjwks.json'",[1259,1974,38],{"class":1265},[1259,1976,1977],{"class":1367},"\u002F\u002F copy from the dashboard\n",[1259,1979,1980],{"class":1261,"line":1397},[1259,1981,1982],{"class":1265},");\n",[1259,1984,1985,1987,1990,1992,1995,1998],{"class":1261,"line":1410},[1259,1986,1953],{"class":1336},[1259,1988,1989],{"class":1269}," CONNECTION_ID",[1259,1991,1388],{"class":1336},[1259,1993,1994],{"class":1276}," 'c1a2e3f4-5678-4abc-9def-0123456789ab'",[1259,1996,1997],{"class":1265},"; ",[1259,1999,2000],{"class":1367},"\u002F\u002F \"Connection ID\" in the Signing keys panel\n",[1259,2002,2003],{"class":1261,"line":1415},[1259,2004,1361],{"emptyLinePlaceholder":1360},[1259,2006,2007],{"class":1261,"line":1421},[1259,2008,2009],{"class":1367},"\u002F\u002F createRemoteJWKSet caches the response and refetches on an unrecognized `kid`.\n",[1259,2011,2012,2014,2017,2019,2022,2025,2027],{"class":1261,"line":1449},[1259,2013,1953],{"class":1336},[1259,2015,2016],{"class":1269}," jwks",[1259,2018,1388],{"class":1336},[1259,2020,2021],{"class":1460}," createRemoteJWKSet",[1259,2023,2024],{"class":1265},"(",[1259,2026,496],{"class":1269},[1259,2028,1982],{"class":1265},[1259,2030,2031],{"class":1261,"line":1454},[1259,2032,1361],{"emptyLinePlaceholder":1360},[1259,2034,2035,2038,2041,2044,2046,2049],{"class":1261,"line":1479},[1259,2036,2037],{"class":1336},"async",[1259,2039,2040],{"class":1336}," function",[1259,2042,2043],{"class":1460}," verifyFiblyToken",[1259,2045,2024],{"class":1265},[1259,2047,2048],{"class":1437},"token",[1259,2050,2051],{"class":1265},") {\n",[1259,2053,2054,2057,2060,2063,2066,2068,2071,2074],{"class":1261,"line":1490},[1259,2055,2056],{"class":1336},"  const",[1259,2058,2059],{"class":1265}," { ",[1259,2061,2062],{"class":1269},"payload",[1259,2064,2065],{"class":1265}," } ",[1259,2067,1427],{"class":1336},[1259,2069,2070],{"class":1336}," await",[1259,2072,2073],{"class":1460}," jwtVerify",[1259,2075,2076],{"class":1265},"(token, jwks, {\n",[1259,2078,2079,2082,2085],{"class":1261,"line":1501},[1259,2080,2081],{"class":1265},"    issuer: ",[1259,2083,2084],{"class":1276},"'fibly'",[1259,2086,1540],{"class":1265},[1259,2088,2089,2092,2094],{"class":1261,"line":1507},[1259,2090,2091],{"class":1265},"    audience: ",[1259,2093,500],{"class":1269},[1259,2095,1540],{"class":1265},[1259,2097,2098,2101,2104],{"class":1261,"line":1513},[1259,2099,2100],{"class":1265},"    algorithms: [",[1259,2102,2103],{"class":1276},"'RS256'",[1259,2105,1527],{"class":1265},[1259,2107,2108],{"class":1261,"line":1530},[1259,2109,2110],{"class":1265},"  });\n",[1259,2112,2113,2116],{"class":1261,"line":1543},[1259,2114,2115],{"class":1336},"  return",[1259,2117,2118],{"class":1265}," payload;\n",[1259,2120,2121],{"class":1261,"line":1555},[1259,2122,2123],{"class":1265},"}\n",[1259,2125,2126],{"class":1261,"line":1593},[1259,2127,1361],{"emptyLinePlaceholder":1360},[1259,2129,2130,2133,2136,2138,2141,2143,2145,2148,2151,2153,2156,2158,2161],{"class":1261,"line":1599},[1259,2131,2132],{"class":1265},"app.",[1259,2134,2135],{"class":1460},"get",[1259,2137,2024],{"class":1265},[1259,2139,2140],{"class":1276},"'\u002Forders\u002F:ref'",[1259,2142,38],{"class":1265},[1259,2144,2037],{"class":1336},[1259,2146,2147],{"class":1265}," (",[1259,2149,2150],{"class":1437},"req",[1259,2152,38],{"class":1265},[1259,2154,2155],{"class":1437},"res",[1259,2157,1826],{"class":1265},[1259,2159,2160],{"class":1336},"=>",[1259,2162,2163],{"class":1265}," {\n",[1259,2165,2166,2168,2171,2173,2176,2179,2181,2184],{"class":1261,"line":1608},[1259,2167,2056],{"class":1336},[1259,2169,2170],{"class":1269}," token",[1259,2172,1388],{"class":1336},[1259,2174,2175],{"class":1265}," req.",[1259,2177,2178],{"class":1460},"header",[1259,2180,2024],{"class":1265},[1259,2182,2183],{"class":1276},"'X-Fibly-Verification'",[1259,2185,1982],{"class":1265},[1259,2187,2188,2191,2193,2196,2199,2202,2205,2208,2210,2213,2216,2218,2221,2224],{"class":1261,"line":1613},[1259,2189,2190],{"class":1336},"  if",[1259,2192,2147],{"class":1265},[1259,2194,2195],{"class":1336},"!",[1259,2197,2198],{"class":1265},"token) ",[1259,2200,2201],{"class":1336},"return",[1259,2203,2204],{"class":1265}," res.",[1259,2206,2207],{"class":1460},"status",[1259,2209,2024],{"class":1265},[1259,2211,2212],{"class":1269},"401",[1259,2214,2215],{"class":1265},").",[1259,2217,1255],{"class":1460},[1259,2219,2220],{"class":1265},"({ error: ",[1259,2222,2223],{"class":1276},"'missing verification token'",[1259,2225,2226],{"class":1265}," });\n",[1259,2228,2229],{"class":1261,"line":1618},[1259,2230,1361],{"emptyLinePlaceholder":1360},[1259,2232,2233,2236],{"class":1261,"line":1649},[1259,2234,2235],{"class":1336},"  let",[1259,2237,2238],{"class":1265}," claims;\n",[1259,2240,2241,2244],{"class":1261,"line":1670},[1259,2242,2243],{"class":1336},"  try",[1259,2245,2163],{"class":1265},[1259,2247,2248,2250,2252,2254,2256],{"class":1261,"line":1682},[1259,2249,1493],{"class":1265},[1259,2251,1427],{"class":1336},[1259,2253,2070],{"class":1336},[1259,2255,2043],{"class":1460},[1259,2257,2258],{"class":1265},"(token);\n",[1259,2260,2261,2264,2267],{"class":1261,"line":1705},[1259,2262,2263],{"class":1265},"  } ",[1259,2265,2266],{"class":1336},"catch",[1259,2268,2163],{"class":1265},[1259,2270,2271,2273,2275,2277,2279,2281,2283,2285,2287,2290],{"class":1261,"line":1713},[1259,2272,1602],{"class":1336},[1259,2274,2204],{"class":1265},[1259,2276,2207],{"class":1460},[1259,2278,2024],{"class":1265},[1259,2280,2212],{"class":1269},[1259,2282,2215],{"class":1265},[1259,2284,1255],{"class":1460},[1259,2286,2220],{"class":1265},[1259,2288,2289],{"class":1276},"'invalid verification token'",[1259,2291,2226],{"class":1265},[1259,2293,2294],{"class":1261,"line":1724},[1259,2295,2296],{"class":1265},"  }\n",[1259,2298,2299],{"class":1261,"line":1735},[1259,2300,1361],{"emptyLinePlaceholder":1360},[1259,2302,2303,2305,2308,2310,2312,2315,2318],{"class":1261,"line":1753},[1259,2304,2056],{"class":1336},[1259,2306,2307],{"class":1269}," order",[1259,2309,1388],{"class":1336},[1259,2311,2070],{"class":1336},[1259,2313,2314],{"class":1460}," getOrder",[1259,2316,2317],{"class":1265},"(req.params.ref); ",[1259,2319,2320],{"class":1367},"\u002F\u002F your own lookup\n",[1259,2322,2323,2325,2327,2329,2332,2334,2336,2338,2340,2342,2344,2346,2348,2351],{"class":1261,"line":1758},[1259,2324,2190],{"class":1336},[1259,2326,2147],{"class":1265},[1259,2328,2195],{"class":1336},[1259,2330,2331],{"class":1265},"order) ",[1259,2333,2201],{"class":1336},[1259,2335,2204],{"class":1265},[1259,2337,2207],{"class":1460},[1259,2339,2024],{"class":1265},[1259,2341,1294],{"class":1269},[1259,2343,2215],{"class":1265},[1259,2345,1255],{"class":1460},[1259,2347,2220],{"class":1265},[1259,2349,2350],{"class":1276},"'not found'",[1259,2352,2226],{"class":1265},[1259,2354,2355],{"class":1261,"line":1772},[1259,2356,1361],{"emptyLinePlaceholder":1360},[1259,2358,2359,2361,2364,2366,2369,2372,2375,2377,2380],{"class":1261,"line":1788},[1259,2360,2056],{"class":1336},[1259,2362,2363],{"class":1269}," verifiedEmail",[1259,2365,1388],{"class":1336},[1259,2367,2368],{"class":1265}," (claims.email ",[1259,2370,2371],{"class":1336},"||",[1259,2373,2374],{"class":1276}," ''",[1259,2376,2215],{"class":1265},[1259,2378,2379],{"class":1460},"toLowerCase",[1259,2381,2382],{"class":1265},"();\n",[1259,2384,2385,2387,2389,2391,2394,2396,2399,2401,2404,2407],{"class":1261,"line":1807},[1259,2386,2190],{"class":1336},[1259,2388,2147],{"class":1265},[1259,2390,2195],{"class":1336},[1259,2392,2393],{"class":1265},"claims.email_verified ",[1259,2395,2371],{"class":1336},[1259,2397,2398],{"class":1265}," order.customerEmail.",[1259,2400,2379],{"class":1460},[1259,2402,2403],{"class":1265},"() ",[1259,2405,2406],{"class":1336},"!==",[1259,2408,2409],{"class":1265}," verifiedEmail) {\n",[1259,2411,2412,2414,2416,2418,2420,2422,2424,2426,2428,2430,2433],{"class":1261,"line":1812},[1259,2413,1602],{"class":1336},[1259,2415,2204],{"class":1265},[1259,2417,2207],{"class":1460},[1259,2419,2024],{"class":1265},[1259,2421,1294],{"class":1269},[1259,2423,2215],{"class":1265},[1259,2425,1255],{"class":1460},[1259,2427,2220],{"class":1265},[1259,2429,2350],{"class":1276},[1259,2431,2432],{"class":1265}," }); ",[1259,2434,2435],{"class":1367},"\u002F\u002F 404, not 403\n",[1259,2437,2438],{"class":1261,"line":1837},[1259,2439,2296],{"class":1265},[1259,2441,2442],{"class":1261,"line":1863},[1259,2443,1361],{"emptyLinePlaceholder":1360},[1259,2445,2446,2449,2451],{"class":1261,"line":1883},[1259,2447,2448],{"class":1265},"  res.",[1259,2450,1255],{"class":1460},[1259,2452,2453],{"class":1265},"({ order_ref: req.params.ref, status: order.status });\n",[1259,2455,2456],{"class":1261,"line":1888},[1259,2457,2458],{"class":1265},"});\n",[1323,2460,2461],{"v-slot:php":1221},[1213,2462,2466],{"className":2463,"code":2464,"language":2465,"meta":1221,"style":1221},"language-php shiki shiki-themes github-dark","\u003C?php\nuse Firebase\\JWT\\JWT;\nuse Firebase\\JWT\\JWK;\n\n\u002F\u002F Copy both values from this connection's Signing keys panel and hardcode them as\n\u002F\u002F configuration — never derive JWKS_URL from a claim inside the (unverified) token\n\u002F\u002F itself, or a token signed by a different tenant's key could validate here.\nconst JWKS_URL = 'https:\u002F\u002Fapp.fibly.io\u002Fapi\u002Ftool-integrations\u002Fcustom-webhooks\u002F\u003Cyour-workspace-id>\u002Fjwks.json'; \u002F\u002F copy from the dashboard\nconst CONNECTION_ID = 'c1a2e3f4-5678-4abc-9def-0123456789ab'; \u002F\u002F \"Connection ID\" in the Signing keys panel\n\nfunction verify_fibly_token(string $token): array\n{\n    \u002F\u002F Cache this response in production instead of fetching on every request.\n    $jwks = json_decode(file_get_contents(JWKS_URL), true);\n    $keys = JWK::parseKeySet($jwks);\n\n    \u002F\u002F JWT::decode verifies the RS256 signature (picking the key by `kid`) and\n    \u002F\u002F enforces `exp` automatically; `iss`\u002F`aud` are checked explicitly below.\n    $claims = (array) JWT::decode($token, $keys);\n\n    if (($claims['iss'] ?? null) !== 'fibly') {\n        throw new \\RuntimeException('unexpected issuer');\n    }\n    if (($claims['aud'] ?? null) !== CONNECTION_ID) {\n        throw new \\RuntimeException('unexpected audience');\n    }\n    return $claims;\n}\n\n\u002F\u002F GET \u002Forders\u002F{ref}\n$token = $_SERVER['HTTP_X_FIBLY_VERIFICATION'] ?? '';\nif ($token === '') {\n    http_response_code(401);\n    echo json_encode(['error' => 'missing verification token']);\n    exit;\n}\n\ntry {\n    $claims = verify_fibly_token($token);\n} catch (\\Throwable $e) {\n    http_response_code(401);\n    echo json_encode(['error' => 'invalid verification token']);\n    exit;\n}\n\n$order = get_order($_GET['ref'] ?? ''); \u002F\u002F your own lookup\nif (!$order) {\n    http_response_code(404);\n    echo json_encode(['error' => 'not found']);\n    exit;\n}\n\n$verifiedEmail = strtolower($claims['email'] ?? '');\n$emailVerified = !empty($claims['email_verified']);\nif (!$emailVerified || strtolower($order['customer_email']) !== $verifiedEmail) {\n    http_response_code(404); \u002F\u002F 404, not 403 — don't confirm the order exists\n    echo json_encode(['error' => 'not found']);\n    exit;\n}\n\necho json_encode(['order_ref' => $order['ref'], 'status' => $order['status']]);\n","php",[114,2467,2468,2476,2486,2495,2499,2503,2507,2511,2526,2540,2544,2564,2569,2574,2600,2619,2623,2628,2633,2658,2662,2690,2707,2712,2735,2750,2754,2761,2765,2769,2774,2795,2810,2821,2844,2851,2855,2859,2866,2877,2892,2902,2919,2925,2930,2935,2963,2975,2986,3004,3011,3016,3021,3046,3067,3096,3110,3127,3134,3139,3144],{"__ignoreMap":1221},[1259,2469,2470,2473],{"class":1261,"line":1262},[1259,2471,2472],{"class":1336},"\u003C?",[1259,2474,2475],{"class":1269},"php\n",[1259,2477,2478,2481,2484],{"class":1261,"line":1343},[1259,2479,2480],{"class":1336},"use",[1259,2482,2483],{"class":1269}," Firebase\\JWT\\JWT",[1259,2485,1929],{"class":1265},[1259,2487,2488,2490,2493],{"class":1261,"line":1357},[1259,2489,2480],{"class":1336},[1259,2491,2492],{"class":1269}," Firebase\\JWT\\JWK",[1259,2494,1929],{"class":1265},[1259,2496,2497],{"class":1261,"line":1364},[1259,2498,1361],{"emptyLinePlaceholder":1360},[1259,2500,2501],{"class":1261,"line":1371},[1259,2502,1938],{"class":1367},[1259,2504,2505],{"class":1261,"line":1377},[1259,2506,1943],{"class":1367},[1259,2508,2509],{"class":1261,"line":1383},[1259,2510,1948],{"class":1367},[1259,2512,2513,2515,2517,2519,2522,2524],{"class":1261,"line":1397},[1259,2514,1953],{"class":1336},[1259,2516,1956],{"class":1269},[1259,2518,1388],{"class":1336},[1259,2520,2521],{"class":1276}," 'https:\u002F\u002Fapp.fibly.io\u002Fapi\u002Ftool-integrations\u002Fcustom-webhooks\u002F\u003Cyour-workspace-id>\u002Fjwks.json'",[1259,2523,1997],{"class":1265},[1259,2525,1977],{"class":1367},[1259,2527,2528,2530,2532,2534,2536,2538],{"class":1261,"line":1410},[1259,2529,1953],{"class":1336},[1259,2531,1989],{"class":1269},[1259,2533,1388],{"class":1336},[1259,2535,1994],{"class":1276},[1259,2537,1997],{"class":1265},[1259,2539,2000],{"class":1367},[1259,2541,2542],{"class":1261,"line":1415},[1259,2543,1361],{"emptyLinePlaceholder":1360},[1259,2545,2546,2549,2551,2553,2555,2558,2561],{"class":1261,"line":1421},[1259,2547,2548],{"class":1336},"function",[1259,2550,1461],{"class":1460},[1259,2552,2024],{"class":1265},[1259,2554,559],{"class":1336},[1259,2556,2557],{"class":1265}," $token)",[1259,2559,2560],{"class":1336},":",[1259,2562,2563],{"class":1336}," array\n",[1259,2565,2566],{"class":1261,"line":1449},[1259,2567,2568],{"class":1265},"{\n",[1259,2570,2571],{"class":1261,"line":1454},[1259,2572,2573],{"class":1367},"    \u002F\u002F Cache this response in production instead of fetching on every request.\n",[1259,2575,2576,2579,2581,2584,2586,2589,2591,2593,2596,2598],{"class":1261,"line":1479},[1259,2577,2578],{"class":1265},"    $jwks ",[1259,2580,1427],{"class":1336},[1259,2582,2583],{"class":1269}," json_decode",[1259,2585,2024],{"class":1265},[1259,2587,2588],{"class":1269},"file_get_contents",[1259,2590,2024],{"class":1265},[1259,2592,496],{"class":1269},[1259,2594,2595],{"class":1265},"), ",[1259,2597,1076],{"class":1269},[1259,2599,1982],{"class":1265},[1259,2601,2602,2605,2607,2610,2613,2616],{"class":1261,"line":1490},[1259,2603,2604],{"class":1265},"    $keys ",[1259,2606,1427],{"class":1336},[1259,2608,2609],{"class":1269}," JWK",[1259,2611,2612],{"class":1336},"::",[1259,2614,2615],{"class":1460},"parseKeySet",[1259,2617,2618],{"class":1265},"($jwks);\n",[1259,2620,2621],{"class":1261,"line":1501},[1259,2622,1361],{"emptyLinePlaceholder":1360},[1259,2624,2625],{"class":1261,"line":1507},[1259,2626,2627],{"class":1367},"    \u002F\u002F JWT::decode verifies the RS256 signature (picking the key by `kid`) and\n",[1259,2629,2630],{"class":1261,"line":1513},[1259,2631,2632],{"class":1367},"    \u002F\u002F enforces `exp` automatically; `iss`\u002F`aud` are checked explicitly below.\n",[1259,2634,2635,2638,2640,2642,2645,2647,2650,2652,2655],{"class":1261,"line":1530},[1259,2636,2637],{"class":1265},"    $claims ",[1259,2639,1427],{"class":1336},[1259,2641,2147],{"class":1265},[1259,2643,2644],{"class":1336},"array",[1259,2646,1826],{"class":1265},[1259,2648,2649],{"class":1269},"JWT",[1259,2651,2612],{"class":1336},[1259,2653,2654],{"class":1460},"decode",[1259,2656,2657],{"class":1265},"($token, $keys);\n",[1259,2659,2660],{"class":1261,"line":1543},[1259,2661,1361],{"emptyLinePlaceholder":1360},[1259,2663,2664,2666,2669,2672,2675,2678,2681,2683,2685,2688],{"class":1261,"line":1555},[1259,2665,1673],{"class":1336},[1259,2667,2668],{"class":1265}," (($claims[",[1259,2670,2671],{"class":1276},"'iss'",[1259,2673,2674],{"class":1265},"] ",[1259,2676,2677],{"class":1336},"??",[1259,2679,2680],{"class":1269}," null",[1259,2682,1826],{"class":1265},[1259,2684,2406],{"class":1336},[1259,2686,2687],{"class":1276}," 'fibly'",[1259,2689,2051],{"class":1265},[1259,2691,2692,2695,2697,2700,2702,2705],{"class":1261,"line":1593},[1259,2693,2694],{"class":1336},"        throw",[1259,2696,1961],{"class":1336},[1259,2698,2699],{"class":1269}," \\RuntimeException",[1259,2701,2024],{"class":1265},[1259,2703,2704],{"class":1276},"'unexpected issuer'",[1259,2706,1982],{"class":1265},[1259,2708,2709],{"class":1261,"line":1599},[1259,2710,2711],{"class":1265},"    }\n",[1259,2713,2714,2716,2718,2721,2723,2725,2727,2729,2731,2733],{"class":1261,"line":1608},[1259,2715,1673],{"class":1336},[1259,2717,2668],{"class":1265},[1259,2719,2720],{"class":1276},"'aud'",[1259,2722,2674],{"class":1265},[1259,2724,2677],{"class":1336},[1259,2726,2680],{"class":1269},[1259,2728,1826],{"class":1265},[1259,2730,2406],{"class":1336},[1259,2732,1989],{"class":1269},[1259,2734,2051],{"class":1265},[1259,2736,2737,2739,2741,2743,2745,2748],{"class":1261,"line":1613},[1259,2738,2694],{"class":1336},[1259,2740,1961],{"class":1336},[1259,2742,2699],{"class":1269},[1259,2744,2024],{"class":1265},[1259,2746,2747],{"class":1276},"'unexpected audience'",[1259,2749,1982],{"class":1265},[1259,2751,2752],{"class":1261,"line":1618},[1259,2753,2711],{"class":1265},[1259,2755,2756,2758],{"class":1261,"line":1649},[1259,2757,1602],{"class":1336},[1259,2759,2760],{"class":1265}," $claims;\n",[1259,2762,2763],{"class":1261,"line":1670},[1259,2764,2123],{"class":1265},[1259,2766,2767],{"class":1261,"line":1682},[1259,2768,1361],{"emptyLinePlaceholder":1360},[1259,2770,2771],{"class":1261,"line":1705},[1259,2772,2773],{"class":1367},"\u002F\u002F GET \u002Forders\u002F{ref}\n",[1259,2775,2776,2779,2781,2784,2787,2789,2791,2793],{"class":1261,"line":1713},[1259,2777,2778],{"class":1265},"$token ",[1259,2780,1427],{"class":1336},[1259,2782,2783],{"class":1265}," $_SERVER[",[1259,2785,2786],{"class":1276},"'HTTP_X_FIBLY_VERIFICATION'",[1259,2788,2674],{"class":1265},[1259,2790,2677],{"class":1336},[1259,2792,2374],{"class":1276},[1259,2794,1929],{"class":1265},[1259,2796,2797,2800,2803,2806,2808],{"class":1261,"line":1724},[1259,2798,2799],{"class":1336},"if",[1259,2801,2802],{"class":1265}," ($token ",[1259,2804,2805],{"class":1336},"===",[1259,2807,2374],{"class":1276},[1259,2809,2051],{"class":1265},[1259,2811,2812,2815,2817,2819],{"class":1261,"line":1735},[1259,2813,2814],{"class":1269},"    http_response_code",[1259,2816,2024],{"class":1265},[1259,2818,2212],{"class":1269},[1259,2820,1982],{"class":1265},[1259,2822,2823,2826,2829,2832,2835,2838,2841],{"class":1261,"line":1753},[1259,2824,2825],{"class":1269},"    echo",[1259,2827,2828],{"class":1269}," json_encode",[1259,2830,2831],{"class":1265},"([",[1259,2833,2834],{"class":1276},"'error'",[1259,2836,2837],{"class":1336}," =>",[1259,2839,2840],{"class":1276}," 'missing verification token'",[1259,2842,2843],{"class":1265},"]);\n",[1259,2845,2846,2849],{"class":1261,"line":1758},[1259,2847,2848],{"class":1336},"    exit",[1259,2850,1929],{"class":1265},[1259,2852,2853],{"class":1261,"line":1772},[1259,2854,2123],{"class":1265},[1259,2856,2857],{"class":1261,"line":1788},[1259,2858,1361],{"emptyLinePlaceholder":1360},[1259,2860,2861,2864],{"class":1261,"line":1807},[1259,2862,2863],{"class":1336},"try",[1259,2865,2163],{"class":1265},[1259,2867,2868,2870,2872,2874],{"class":1261,"line":1812},[1259,2869,2637],{"class":1265},[1259,2871,1427],{"class":1336},[1259,2873,1461],{"class":1460},[1259,2875,2876],{"class":1265},"($token);\n",[1259,2878,2879,2882,2884,2886,2889],{"class":1261,"line":1837},[1259,2880,2881],{"class":1265},"} ",[1259,2883,2266],{"class":1336},[1259,2885,2147],{"class":1265},[1259,2887,2888],{"class":1269},"\\Throwable",[1259,2890,2891],{"class":1265}," $e) {\n",[1259,2893,2894,2896,2898,2900],{"class":1261,"line":1863},[1259,2895,2814],{"class":1269},[1259,2897,2024],{"class":1265},[1259,2899,2212],{"class":1269},[1259,2901,1982],{"class":1265},[1259,2903,2904,2906,2908,2910,2912,2914,2917],{"class":1261,"line":1883},[1259,2905,2825],{"class":1269},[1259,2907,2828],{"class":1269},[1259,2909,2831],{"class":1265},[1259,2911,2834],{"class":1276},[1259,2913,2837],{"class":1336},[1259,2915,2916],{"class":1276}," 'invalid verification token'",[1259,2918,2843],{"class":1265},[1259,2920,2921,2923],{"class":1261,"line":1888},[1259,2922,2848],{"class":1336},[1259,2924,1929],{"class":1265},[1259,2926,2928],{"class":1261,"line":2927},44,[1259,2929,2123],{"class":1265},[1259,2931,2933],{"class":1261,"line":2932},45,[1259,2934,1361],{"emptyLinePlaceholder":1360},[1259,2936,2938,2941,2943,2946,2949,2952,2954,2956,2958,2961],{"class":1261,"line":2937},46,[1259,2939,2940],{"class":1265},"$order ",[1259,2942,1427],{"class":1336},[1259,2944,2945],{"class":1460}," get_order",[1259,2947,2948],{"class":1265},"($_GET[",[1259,2950,2951],{"class":1276},"'ref'",[1259,2953,2674],{"class":1265},[1259,2955,2677],{"class":1336},[1259,2957,2374],{"class":1276},[1259,2959,2960],{"class":1265},"); ",[1259,2962,2320],{"class":1367},[1259,2964,2966,2968,2970,2972],{"class":1261,"line":2965},47,[1259,2967,2799],{"class":1336},[1259,2969,2147],{"class":1265},[1259,2971,2195],{"class":1336},[1259,2973,2974],{"class":1265},"$order) {\n",[1259,2976,2978,2980,2982,2984],{"class":1261,"line":2977},48,[1259,2979,2814],{"class":1269},[1259,2981,2024],{"class":1265},[1259,2983,1294],{"class":1269},[1259,2985,1982],{"class":1265},[1259,2987,2989,2991,2993,2995,2997,2999,3002],{"class":1261,"line":2988},49,[1259,2990,2825],{"class":1269},[1259,2992,2828],{"class":1269},[1259,2994,2831],{"class":1265},[1259,2996,2834],{"class":1276},[1259,2998,2837],{"class":1336},[1259,3000,3001],{"class":1276}," 'not found'",[1259,3003,2843],{"class":1265},[1259,3005,3007,3009],{"class":1261,"line":3006},50,[1259,3008,2848],{"class":1336},[1259,3010,1929],{"class":1265},[1259,3012,3014],{"class":1261,"line":3013},51,[1259,3015,2123],{"class":1265},[1259,3017,3019],{"class":1261,"line":3018},52,[1259,3020,1361],{"emptyLinePlaceholder":1360},[1259,3022,3024,3027,3029,3032,3035,3038,3040,3042,3044],{"class":1261,"line":3023},53,[1259,3025,3026],{"class":1265},"$verifiedEmail ",[1259,3028,1427],{"class":1336},[1259,3030,3031],{"class":1269}," strtolower",[1259,3033,3034],{"class":1265},"($claims[",[1259,3036,3037],{"class":1276},"'email'",[1259,3039,2674],{"class":1265},[1259,3041,2677],{"class":1336},[1259,3043,2374],{"class":1276},[1259,3045,1982],{"class":1265},[1259,3047,3049,3052,3054,3057,3060,3062,3065],{"class":1261,"line":3048},54,[1259,3050,3051],{"class":1265},"$emailVerified ",[1259,3053,1427],{"class":1336},[1259,3055,3056],{"class":1336}," !",[1259,3058,3059],{"class":1269},"empty",[1259,3061,3034],{"class":1265},[1259,3063,3064],{"class":1276},"'email_verified'",[1259,3066,2843],{"class":1265},[1259,3068,3070,3072,3074,3076,3078,3080,3082,3085,3088,3091,3093],{"class":1261,"line":3069},55,[1259,3071,2799],{"class":1336},[1259,3073,2147],{"class":1265},[1259,3075,2195],{"class":1336},[1259,3077,3051],{"class":1265},[1259,3079,2371],{"class":1336},[1259,3081,3031],{"class":1269},[1259,3083,3084],{"class":1265},"($order[",[1259,3086,3087],{"class":1276},"'customer_email'",[1259,3089,3090],{"class":1265},"]) ",[1259,3092,2406],{"class":1336},[1259,3094,3095],{"class":1265}," $verifiedEmail) {\n",[1259,3097,3099,3101,3103,3105,3107],{"class":1261,"line":3098},56,[1259,3100,2814],{"class":1269},[1259,3102,2024],{"class":1265},[1259,3104,1294],{"class":1269},[1259,3106,2960],{"class":1265},[1259,3108,3109],{"class":1367},"\u002F\u002F 404, not 403 — don't confirm the order exists\n",[1259,3111,3113,3115,3117,3119,3121,3123,3125],{"class":1261,"line":3112},57,[1259,3114,2825],{"class":1269},[1259,3116,2828],{"class":1269},[1259,3118,2831],{"class":1265},[1259,3120,2834],{"class":1276},[1259,3122,2837],{"class":1336},[1259,3124,3001],{"class":1276},[1259,3126,2843],{"class":1265},[1259,3128,3130,3132],{"class":1261,"line":3129},58,[1259,3131,2848],{"class":1336},[1259,3133,1929],{"class":1265},[1259,3135,3137],{"class":1261,"line":3136},59,[1259,3138,2123],{"class":1265},[1259,3140,3142],{"class":1261,"line":3141},60,[1259,3143,1361],{"emptyLinePlaceholder":1360},[1259,3145,3147,3150,3152,3154,3157,3159,3162,3164,3167,3170,3172,3174,3176],{"class":1261,"line":3146},61,[1259,3148,3149],{"class":1269},"echo",[1259,3151,2828],{"class":1269},[1259,3153,2831],{"class":1265},[1259,3155,3156],{"class":1276},"'order_ref'",[1259,3158,2837],{"class":1336},[1259,3160,3161],{"class":1265}," $order[",[1259,3163,2951],{"class":1276},[1259,3165,3166],{"class":1265},"], ",[1259,3168,3169],{"class":1276},"'status'",[1259,3171,2837],{"class":1336},[1259,3173,3161],{"class":1265},[1259,3175,3169],{"class":1276},[1259,3177,3178],{"class":1265},"]]);\n",[54,3180,3182],{"id":3181},"identity-verification-confirmation","Identity verification & confirmation",[16,3184,3185],{},"Two independent, per-tool toggles control how cautiously a tool is called:",[394,3187,3189,3190,421],{"id":3188},"identity-verification-requires_verification","Identity verification (",[114,3191,3192],{},"requires_verification",[16,3194,424,3195,3197,3198,3200,3201,88],{},[20,3196,310],{}," for a tool that returns private data (order details,\naccount information, anything tied to one customer). While it's on, the agent can only call your\nwebhook once the visitor has proven ownership of their email — either via Fibly's own\nverification link, or because the conversation itself came in over a verified email channel.\nUntil then, calling the tool returns a ",[114,3199,1207],{}," status to the model, which asks\nthe customer to verify and retries — ",[20,3202,3203],{},"your webhook is never called for an unverified attempt",[16,3205,3206,3207,3210,3211,3213,3214,357,3216,3218],{},"This only proves the caller controls ",[49,3208,3209],{},"an"," email address. It does ",[20,3212,391],{}," prove that the specific\nresource being requested (e.g. this particular order) belongs to them — that check is always\nyours to make, using the verified ",[114,3215,771],{},[114,3217,831],{}," claims in the token, as shown in the\nworked example above.",[394,3220,3222,3223,421],{"id":3221},"confirmation-requires_confirmation","Confirmation (",[114,3224,3225],{},"requires_confirmation",[16,3227,424,3228,3230],{},[20,3229,300],{}," for a tool that changes something (creating a ticket, cancelling\nan order, anything you don't want fired silently). While it's on, the widget pauses and asks the\ncustomer to explicitly approve the specific call before it reaches your webhook. With it off, the\ntool fires immediately whenever the model decides to use it — appropriate for read-only lookups.",[54,3232,3234],{"id":3233},"testing-your-tool","Testing your tool",[16,3236,3237,3238,3241,3242,3245,3246,3249,3250,3252,3253,3255],{},"Before enabling a tool for real conversations, open it in the builder and use the ",[20,3239,3240],{},"Test this\ntool"," panel: enter sample arguments as JSON, then click ",[20,3243,3244],{},"Run test",". This sends a ",[20,3247,3248],{},"real"," call\nthrough the exact same path production traffic uses — SSRF-checked, and with a live\n",[114,3251,167],{}," token attached if the connection's ",[20,3254,163],{}," setting is\non — and shows you the resulting status and response inline, so you can confirm your webhook is\nwired up correctly before turning it on.",[54,3257,3259],{"id":3258},"security-checklist","Security checklist",[99,3261,3262,3271,3279,3285,3298,3304,3310],{},[76,3263,3264,3267,3268,3270],{},[20,3265,3266],{},"Always verify the signature."," Never trust the claims in ",[114,3269,167],{}," until the\nRS256 signature checks out against your JWKS.",[76,3272,3273,3278],{},[20,3274,3275,3276,88],{},"Check ",[114,3277,643],{}," Reject expired tokens; most JWT libraries do this automatically once you decode\nwith verification.",[76,3280,3281,3284],{},[20,3282,3283],{},"Enforce resource ownership yourself."," Fibly proves who the verified visitor is; it does not\nknow your data model, so it cannot confirm that the record they're asking about belongs to them.",[76,3286,3287,3293,3294,1073,3296,88],{},[20,3288,3289,3290,3292],{},"Don't trust ",[114,3291,771],{}," on its own."," Only treat it as verified when ",[114,3295,787],{},[114,3297,1076],{},[76,3299,3300,3303],{},[20,3301,3302],{},"Use HTTPS"," on your endpoint — Fibly requires it (except for a workspace's own local\ndevelopment, which isn't reachable from the internet anyway).",[76,3305,3306,3309],{},[20,3307,3308],{},"Protect your connection's secret."," Treat your Basic\u002FBearer credential like a password; Fibly\nencrypts it at rest and never displays it again after you save it.",[76,3311,3312,3315],{},[20,3313,3314],{},"Prefer 404 over 403"," when an ownership check fails, so you don't confirm to a caller that a\nresource exists.",[54,3317,3319],{"id":3318},"related-articles","Related articles",[99,3321,3322,3329],{},[76,3323,3324,3328],{},[34,3325,3327],{"href":3326},"\u002Fhelp\u002Ftools","Tools overview",": all the tools your bot can use.",[76,3330,3331,3335],{},[34,3332,3334],{"href":3333},"\u002Fhelp\u002Fconversations","Conversations",": see the actions the bot took, including custom tool calls.",[3337,3338,3339],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":1221,"searchDepth":1343,"depth":1343,"links":3341},[3342,3343,3344,3345,3348,3357,3358,3364,3365,3366],{"id":56,"depth":1343,"text":57},{"id":63,"depth":1343,"text":64},{"id":189,"depth":1343,"text":190},{"id":330,"depth":1343,"text":331,"children":3346},[3347],{"id":396,"depth":1357,"text":397},{"id":417,"depth":1343,"text":3349,"children":3350},"Verifying the caller (X-Fibly-Verification)",[3351,3352,3353,3355,3356],{"id":455,"depth":1357,"text":456},{"id":512,"depth":1357,"text":513},{"id":896,"depth":1357,"text":3354},"Channel data (channel_data)",{"id":1089,"depth":1357,"text":1090},{"id":1167,"depth":1357,"text":1168},{"id":1298,"depth":1343,"text":1299},{"id":3181,"depth":1343,"text":3182,"children":3359},[3360,3362],{"id":3188,"depth":1357,"text":3361},"Identity verification (requires_verification)",{"id":3221,"depth":1357,"text":3363},"Confirmation (requires_confirmation)",{"id":3233,"depth":1343,"text":3234},{"id":3258,"depth":1343,"text":3259},{"id":3318,"depth":1343,"text":3319},"Turn your own HTTP API into agent tools — build connections and tools in the dashboard, then verify the signed request on your server.","md","tools\u002Fcustom-webhook-tools",{},"\u002Fhelp\u002Ftools\u002Fcustom-webhook-tools",{"title":5,"description":3367},"help\u002Ftools\u002Fcustom-webhook-tools","pZy_kIxrpE7pLE6CEjrxnwGsjcnWRxYewykhjIqFdwE",[3376,3381,3384,3390,3395,3398,3403,3408,3413,3418,3423,3428,3433,3438,3443,3448,3453,3458,3463,3468,3473,3478,3483,3488,3493,3498,3501,3506,3511,3516,3521,3522,3527,3532,3537,3542,3547,3552,3557,3562,3567,3572,3577,3582,3587,3592,3597,3602,3607,3612,3617,3622,3627,3632,3637,3642,3647,3652,3657,3662,3667,3672,3677,3682,3686,3689,3694,3699,3704,3709,3714,3720],{"path":3377,"title":3378,"description":3379,"order":3380,"key":3380},"\u002Fhelp\u002Fconversations\u002Fconversation-details","Conversation details","[object Object]",null,{"path":3382,"title":3383,"description":3379,"order":3380,"key":3380},"\u002Fhelp\u002Fteam-and-account\u002Ftwo-factor-authentication","Two-factor authentication",{"path":3385,"title":3386,"description":3387,"order":3388,"key":3389},"\u002Fhelp","Fibly Help Center","Guides, instructions, and answers to questions about setting up and working with Fibly day to day.",0,"index",{"path":3391,"title":3392,"description":3393,"order":1262,"key":3394},"\u002Fhelp\u002Fbot-configuration\u002Fbrand","Brand","How to set your website address and a short brand description, the two fields the Fibly bot uses as its basic context.","bot-configuration\u002Fbrand",{"path":36,"title":37,"description":3396,"order":1262,"key":3397},"Connect Calendly so the bot can check your availability, share booking links, and book meetings during a conversation.","tools\u002Fcalendly",{"path":3399,"title":3400,"description":3401,"order":1262,"key":3402},"\u002Fhelp\u002Fcustomers\u002Fcustomer-list","Customer list","How to view, filter, and search customers in the Fibly dashboard.","customers\u002Fcustomer-list",{"path":3404,"title":3405,"description":3406,"order":1262,"key":3407},"\u002Fhelp\u002Fchannels\u002Femail","Email","Connect your support mailboxes to Fibly over IMAP and SMTP so incoming mail becomes tickets the bot and your team can answer.","channels\u002Femail",{"path":3409,"title":3410,"description":3411,"order":1262,"key":3412},"\u002Fhelp\u002Fgetting-started\u002Fhow-the-bot-works","How the Fibly bot works","An overview of the architecture, from the customer's question to handing the matter over to your team.","getting-started\u002Fhow-the-bot-works",{"path":3414,"title":3415,"description":3416,"order":1262,"key":3417},"\u002Fhelp\u002Finstallation\u002Fwordpress","Installing on WordPress","Three ways to add the Fibly widget to a WordPress-based site.","installation\u002Fwordpress",{"path":3419,"title":3420,"description":3421,"order":1262,"key":3422},"\u002Fhelp\u002Fconversations\u002Flist-and-filters","List and filters","How to filter, sort, and find conversations in the Fibly dashboard.","conversations\u002Flist-and-filters",{"path":3424,"title":3425,"description":3426,"order":1262,"key":3427},"\u002Fhelp\u002Fanalytics\u002Foverview","Overview","What the metric tiles and charts on the Fibly panel's home screen show.","analytics\u002Foverview",{"path":3429,"title":3430,"description":3431,"order":1262,"key":3432},"\u002Fhelp\u002Fbilling\u002Fplans","Plans","A comparison of the Starter, Professional, and Enterprise plans in Fibly, plus the available add-ons.","billing\u002Fplans",{"path":3434,"title":3435,"description":3436,"order":1262,"key":3437},"\u002Fhelp\u002Flive-support\u002Ftaking-over-a-conversation","Taking over a conversation","How to take a conversation over from the bot and handle it personally as an agent.","live-support\u002Ftaking-over-a-conversation",{"path":3439,"title":3440,"description":3441,"order":1262,"key":3442},"\u002Fhelp\u002Fteam-and-account\u002Fteam-members","Team members","How to invite, edit, and remove a user in the Fibly dashboard.","team-and-account\u002Fteam-members",{"path":3444,"title":3445,"description":3446,"order":1262,"key":3447},"\u002Fhelp\u002Fwidget\u002Fappearance","Widget appearance","How to match the colors, button text, and chat window title to your brand.","widget\u002Fappearance",{"path":3449,"title":3450,"description":3451,"order":1262,"key":3452},"\u002Fhelp\u002Ftroubleshooting\u002Fwidget-not-appearing","Widget not appearing","What to check when the chat button doesn't show up after embedding the script on your site.","troubleshooting\u002Fwidget-not-appearing",{"path":3454,"title":3455,"description":3456,"order":1343,"key":3457},"\u002Fhelp\u002Ftroubleshooting\u002Fbot-gives-wrong-answers","Bot gives wrong answers","What to do when the bot answers inaccurately, mixes up facts, or makes things up.","troubleshooting\u002Fbot-gives-wrong-answers",{"path":3459,"title":3460,"description":3461,"order":1343,"key":3462},"\u002Fhelp\u002Fanalytics\u002Fcontrol-center","Control Center","Advanced escalation analytics, bot confidence breakdown, and recommendations in the Fibly panel.","analytics\u002Fcontrol-center",{"path":3464,"title":3465,"description":3466,"order":1343,"key":3467},"\u002Fhelp\u002Fbilling\u002Fcredits-and-usage","Credits and usage","What AI credits are, how many a single bot message consumes, and where to check your current usage.","billing\u002Fcredits-and-usage",{"path":3469,"title":3470,"description":3471,"order":1343,"key":3472},"\u002Fhelp\u002Fcustomers\u002Fcustomer-profile","Customer profile","How to edit customer details, add notes, review conversation history, and anonymize data in line with GDPR.","customers\u002Fcustomer-profile",{"path":3474,"title":3475,"description":3476,"order":1343,"key":3477},"\u002Fhelp\u002Flive-support\u002Fending-a-conversation","Ending a conversation","How to hand a conversation back to the bot or close it with a resolution outcome.","live-support\u002Fending-a-conversation",{"path":3479,"title":3480,"description":3481,"order":1343,"key":3482},"\u002Fhelp\u002Fgetting-started\u002Ffirst-login","First login","How to accept your Fibly invitation and get access to the admin dashboard.","getting-started\u002Ffirst-login",{"path":3484,"title":3485,"description":3486,"order":1343,"key":3487},"\u002Fhelp\u002Fknowledge-base\u002Fimport-markdown","Import Markdown files","How to import existing documentation in Markdown format into the Fibly knowledge base.","knowledge-base\u002Fimport-markdown",{"path":3489,"title":3490,"description":3491,"order":1343,"key":3492},"\u002Fhelp\u002Finstallation\u002Fshopify","Installing on Shopify","How to add the Fibly widget to a Shopify store by editing the theme.liquid file.","installation\u002Fshopify",{"path":3494,"title":3495,"description":3496,"order":1343,"key":3497},"\u002Fhelp\u002Fbot-configuration\u002Fmessages","Messages","How to set the bot's first message and choose one of three conversational personalities.","bot-configuration\u002Fmessages",{"path":41,"title":42,"description":3499,"order":1343,"key":3500},"Connect PrestaShop so the bot can look up order status, product details, and stock levels for your customers during a conversation.","tools\u002Fprestashop",{"path":3502,"title":3503,"description":3504,"order":1343,"key":3505},"\u002Fhelp\u002Fteam-and-account\u002Froles-and-permissions","Roles and permissions","What the Admin, Manager, and Agent can do in the Fibly dashboard.","team-and-account\u002Froles-and-permissions",{"path":3507,"title":3508,"description":3509,"order":1343,"key":3510},"\u002Fhelp\u002Fwidget\u002Fwelcome-notification","Welcome notification","How to set a short message that encourages the customer to open the chat.","widget\u002Fwelcome-notification",{"path":3512,"title":3513,"description":3514,"order":1343,"key":3515},"\u002Fhelp\u002Fchannels\u002Fwhatsapp","WhatsApp","Connect your WhatsApp Business number so Fibly answers your customers on WhatsApp, with every chat in the same inbox.","channels\u002Fwhatsapp",{"path":3517,"title":3518,"description":3519,"order":1357,"key":3520},"\u002Fhelp\u002Fconversations\u002Fclosing-a-conversation","Closing a conversation","How to mark finished conversations as resolved or unresolved, and why it matters.","conversations\u002Fclosing-a-conversation",{"path":3371,"title":5,"description":3367,"order":1357,"key":3369},{"path":3523,"title":3524,"description":3525,"order":1357,"key":3526},"\u002Fhelp\u002Fchannels\u002Fmessenger","Facebook Messenger","Connect your Facebook Pages so Fibly replies to Messenger messages, all in the same inbox as your other channels.","channels\u002Fmessenger",{"path":3528,"title":3529,"description":3530,"order":1357,"key":3531},"\u002Fhelp\u002Fbot-configuration\u002Fhuman-support","Human support","How to configure handing conversations over to your team when the bot can't handle a question.","bot-configuration\u002Fhuman-support",{"path":3533,"title":3534,"description":3535,"order":1357,"key":3536},"\u002Fhelp\u002Finstallation\u002Fwix","Installing on Wix","How to add the Fibly widget to a Wix site through the site settings panel.","installation\u002Fwix",{"path":3538,"title":3539,"description":3540,"order":1357,"key":3541},"\u002Fhelp\u002Fwidget\u002Fnotification-sound","Notification sound","How to choose the audio cue the widget plays for the customer when new messages arrive.","widget\u002Fnotification-sound",{"path":3543,"title":3544,"description":3545,"order":1357,"key":3546},"\u002Fhelp\u002Fgetting-started\u002Fquick-start","Quick start","A checklist for first launching the bot, from logging in to the first conversation.","getting-started\u002Fquick-start",{"path":3548,"title":3549,"description":3550,"order":1357,"key":3551},"\u002Fhelp\u002Fbilling\u002Fteam-seats","Team seats","What the seat limit means, how to buy additional seats, and what happens when you exceed the limit.","billing\u002Fteam-seats",{"path":3553,"title":3554,"description":3555,"order":1357,"key":3556},"\u002Fhelp\u002Fteam-and-account\u002Fuser-profile","User profile","How to change your first name, last name, and review your account details in the Fibly dashboard.","team-and-account\u002Fuser-profile",{"path":3558,"title":3559,"description":3560,"order":1357,"key":3561},"\u002Fhelp\u002Fknowledge-base\u002Fmanually-written-article","Writing articles manually","How to write knowledge base articles in the Fibly editor, in visual or source mode.","knowledge-base\u002Fmanually-written-article",{"path":3563,"title":3564,"description":3565,"order":1364,"key":3566},"\u002Fhelp\u002Fconversations\u002Fai-assist","AI assist for agents","How to generate a draft reply based on your knowledge base while handling a conversation.","conversations\u002Fai-assist",{"path":3568,"title":3569,"description":3570,"order":1364,"key":3571},"\u002Fhelp\u002Fteam-and-account\u002Fchanging-password","Changing your password","How to update the password for your Fibly account.","team-and-account\u002Fchanging-password",{"path":3573,"title":3574,"description":3575,"order":1364,"key":3576},"\u002Fhelp\u002Fwidget\u002Fenabling-and-disabling","Enabling and disabling the widget","How to temporarily hide the chat button without removing the code from your site.","widget\u002Fenabling-and-disabling",{"path":3578,"title":3579,"description":3580,"order":1364,"key":3581},"\u002Fhelp\u002Fchannels\u002Finstagram","Instagram","Connect your Instagram Professional account so Fibly replies to Instagram direct messages, all from one inbox.","channels\u002Finstagram",{"path":3583,"title":3584,"description":3585,"order":1364,"key":3586},"\u002Fhelp\u002Finstallation\u002Fprestashop","Installing on PrestaShop","How to install the official Fibly module in a PrestaShop store.","installation\u002Fprestashop",{"path":3588,"title":3589,"description":3590,"order":1364,"key":3591},"\u002Fhelp\u002Fbilling\u002Finvoices","Invoices","Where to find your invoices, what their payment statuses mean, and how to download a PDF.","billing\u002Finvoices",{"path":3593,"title":3594,"description":3595,"order":1364,"key":3596},"\u002Fhelp\u002Fbot-configuration\u002Fknowledge-base-context","Knowledge base context","How to set the depth of prerequisites the bot takes into account when answering.","bot-configuration\u002Fknowledge-base-context",{"path":3598,"title":3599,"description":3600,"order":1364,"key":3601},"\u002Fhelp\u002Ftroubleshooting\u002Fno-email-notifications","No email notifications","What to check when you're not receiving escalation emails or the weekly summary.","troubleshooting\u002Fno-email-notifications",{"path":3603,"title":3604,"description":3605,"order":1364,"key":3606},"\u002Fhelp\u002Fknowledge-base\u002Fpinning-articles","Pinning articles","How to choose up to three key articles that your Fibly bot reads before every answer.","knowledge-base\u002Fpinning-articles",{"path":3608,"title":3609,"description":3610,"order":1371,"key":3611},"\u002Fhelp\u002Fbilling\u002Fchanging-plan","Changing your plan","How to move to a higher or lower plan in Fibly.","billing\u002Fchanging-plan",{"path":3613,"title":3614,"description":3615,"order":1371,"key":3616},"\u002Fhelp\u002Ftroubleshooting\u002Fconversation-not-in-live-support","Conversation not appearing in Live support","Diagnosing the situation where a customer asks for a human but the conversation doesn't reach the agent queue.","troubleshooting\u002Fconversation-not-in-live-support",{"path":3618,"title":3619,"description":3620,"order":1371,"key":3621},"\u002Fhelp\u002Finstallation\u002Fsquarespace","Installing on Squarespace","How to embed the Fibly widget on a Squarespace site through the \"Code Injection\" section.","installation\u002Fsquarespace",{"path":3623,"title":3624,"description":3625,"order":1371,"key":3626},"\u002Fhelp\u002Fknowledge-base\u002Fprerequisites","Prerequisites","How to link knowledge base articles into groups that your Fibly bot reads together.","knowledge-base\u002Fprerequisites",{"path":3628,"title":3629,"description":3630,"order":1377,"key":3631},"\u002Fhelp\u002Fknowledge-base\u002Fcontent-quality","Content quality","How Fibly automatically assesses knowledge base articles and what each status means.","knowledge-base\u002Fcontent-quality",{"path":3633,"title":3634,"description":3635,"order":1377,"key":3636},"\u002Fhelp\u002Finstallation\u002Fdomain-verification","Domain verification","How to confirm that you own the domain where you're embedding the Fibly widget.","installation\u002Fdomain-verification",{"path":3638,"title":3639,"description":3640,"order":1377,"key":3641},"\u002Fhelp\u002Finstallation\u002Fcustom-website","Installing on a custom website","How to embed the Fibly widget in plain HTML, Next.js, Nuxt, Vue, and React.","installation\u002Fcustom-website",{"path":3643,"title":3644,"description":3645,"order":1377,"key":3646},"\u002Fhelp\u002Fteam-and-account\u002Fweekly-summary","Weekly summary","A weekly email with metrics on your bot's performance and improvement suggestions.","team-and-account\u002Fweekly-summary",{"path":3648,"title":3649,"description":3650,"order":1383,"key":3651},"\u002Fhelp\u002Fknowledge-base\u002Fbest-practices","Best practices","Tips for writing, organizing, and maintaining your Fibly bot's knowledge base.","knowledge-base\u002Fbest-practices",{"path":3653,"title":3654,"description":3655,"order":1383,"key":3656},"\u002Fhelp\u002Finstallation\u002Finstallation-verification","Installation verification","How to check that the Fibly widget works correctly after embedding it on your site.","installation\u002Finstallation-verification",{"path":3658,"title":3659,"description":3660,"order":1415,"key":3661},"\u002Fhelp\u002Fgetting-started","Getting started","What Fibly is, how the bot works, and what to prepare before your first login.","getting-started\u002Findex",{"path":3663,"title":3664,"description":3665,"order":1543,"key":3666},"\u002Fhelp\u002Finstallation","Widget installation","How to embed the Fibly widget on your website. Ready-made instructions for the most popular platforms.","installation\u002Findex",{"path":3668,"title":3669,"description":3670,"order":1613,"key":3671},"\u002Fhelp\u002Fwidget","Widget","How to customize the appearance, content, and behavior of the chat widget on your website.","widget\u002Findex",{"path":3673,"title":3674,"description":3675,"order":1670,"key":3676},"\u002Fhelp\u002Fchannels","Channels","Connect Fibly to the places your customers already write to you — web chat, email, WhatsApp, Facebook Messenger, and Instagram — and answer everything from one inbox.","channels\u002Findex",{"path":3678,"title":3679,"description":3680,"order":1705,"key":3681},"\u002Fhelp\u002Fknowledge-base","Knowledge base","How to build and maintain the knowledge base your Fibly bot draws its answers from.","knowledge-base\u002Findex",{"path":3326,"title":3683,"description":3684,"order":1724,"key":3685},"Tools","Connect tools so your bot can take actions during a conversation — like checking availability and booking meetings with Calendly.","tools\u002Findex",{"path":3333,"title":3334,"description":3687,"order":1758,"key":3688},"How to browse, filter, and analyze customer conversations with the bot and your team.","conversations\u002Findex",{"path":3690,"title":3691,"description":3692,"order":1837,"key":3693},"\u002Fhelp\u002Fbot-configuration","Bot configuration","How to set the brand, tone of voice, escalation behavior, and answer context for the Fibly bot.","bot-configuration\u002Findex",{"path":3695,"title":3696,"description":3697,"order":2932,"key":3698},"\u002Fhelp\u002Flive-support","Live support","How to handle conversations waiting for an agent, take them over from the bot, and end them.","live-support\u002Findex",{"path":3700,"title":3701,"description":3702,"order":3006,"key":3703},"\u002Fhelp\u002Fanalytics","Analytics","How to read the metrics and charts about your bot's and your team's work in the Fibly panel.","analytics\u002Findex",{"path":3705,"title":3706,"description":3707,"order":3069,"key":3708},"\u002Fhelp\u002Fcustomers","Customers","How to view and manage the profiles of people who have talked to the bot on your website.","customers\u002Findex",{"path":3710,"title":3711,"description":3712,"order":3141,"key":3713},"\u002Fhelp\u002Fteam-and-account","Team and account","How to manage team members, roles, your own profile, and access security.","team-and-account\u002Findex",{"path":3715,"title":3716,"description":3717,"order":3718,"key":3719},"\u002Fhelp\u002Fbilling","Billing","How plans, AI credits, team seats, and invoices work in Fibly.",65,"billing\u002Findex",{"path":3721,"title":3722,"description":3723,"order":3724,"key":3725},"\u002Fhelp\u002Ftroubleshooting","Troubleshooting","Diagnosing the most common issues with the widget and the Fibly dashboard.",999,"troubleshooting\u002Findex",1787310387542]