[{"data":1,"prerenderedAt":171},["ShallowReactive",2],{"doc:\u002Fdocs\u002Fquick-start":3},{"page":4,"toc":156,"updated":170},{"path":5,"title":6,"seoTitle":7,"description":8,"blocks":9},"\u002Fdocs\u002Fquick-start","Quick start","Quick Start: Identify a Visitor and Verify It on Your Server","Create keys, identify a visitor in the browser, read the result with a secret key on your server, and act on it. The whole integration in five steps.",[10,13,126,131,133,136,143],{"type":11,"text":12},"p","This guide takes you from an empty account to a verified identification. It uses the browser SDK and Node.js; every step has the same shape in other languages. New accounts start with $3 of credit, and development keys are free.",{"type":14,"steps":15},"steps",[16,21,33,55,101],{"title":17,"blocks":18},"Create an account and choose a region",[19],{"type":11,"text":20},"Sign up for the Fingerly dashboard. The owner of a new organization chooses where its visitor data will live, and the choice is permanent. See [regions](\u002Fdocs\u002Fregions).",{"title":22,"blocks":23},"Create a public key",[24,26],{"type":11,"text":25},"In **Integration > SDK keys**, create a **public** key in the **development** environment and add the origin your app runs on. A public key with no allowed origins refuses every request.",{"type":27,"samples":28},"code",[29],{"label":30,"lang":31,"code":32},"Allowed origin","text","http:\u002F\u002Flocalhost:3000",{"title":34,"blocks":35},"Identify a visitor in the browser",[36,45,51],{"type":27,"samples":37},[38,42],{"label":39,"lang":40,"code":41},"npm","bash","npm install @fingerly\u002Fweb-js",{"label":43,"lang":40,"code":44},"pnpm","pnpm add @fingerly\u002Fweb-js",{"type":27,"samples":46},[47],{"label":48,"lang":49,"code":50},"login.ts","ts","import { load } from '@fingerly\u002Fweb-js'\n\nconst fingerly = await load({ apiKey: 'fly_pk_us_development_…' })\n\nform.addEventListener('submit', async (event) => {\n  event.preventDefault()\n  const { requestId } = await fingerly.identify({ tag: 'login' })\n\n  await fetch('\u002Fapi\u002Flogin', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application\u002Fjson' },\n    body: JSON.stringify({ email: form.email.value, password: form.password.value, requestId }),\n  })\n})",{"type":52,"tone":53,"text":54},"callout","tip","Using React, Vue, Next.js or a mobile app? The same step is in each [SDK's guide](\u002Fdocs\u002Fsdks).",{"title":56,"blocks":57},"Create a secret key and read the result on your server",[58,60,62],{"type":11,"text":59},"Create a **secret** key in the same environment and store it in your server's environment. The secret is shown once.",{"type":11,"text":61},"When the login request reaches your backend, read the event its `requestId` names.",{"type":27,"samples":63},[64,67,71,74,78,82,86,90,94,98],{"label":65,"lang":49,"code":66},"Node.js","import { load } from '@fingerly\u002Fnode'\n\nconst fingerly = load({ secretKey: process.env.FINGERLY_SECRET_KEY! })\nconst event = await fingerly.events.get('01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4')",{"label":68,"lang":69,"code":70},"Python","python","import os\nfrom fingerly import Fingerly\n\nfingerly = Fingerly(secret_key=os.environ[\"FINGERLY_SECRET_KEY\"])\nevent = fingerly.events.get(\"01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\")",{"label":72,"lang":69,"code":73},"Python (async)","import os\nfrom fingerly import AsyncFingerly\n\nfingerly = AsyncFingerly(secret_key=os.environ[\"FINGERLY_SECRET_KEY\"])\nevent = await fingerly.events.get(\"01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\")",{"label":75,"lang":76,"code":77},"Go","go","import fingerly \"github.com\u002Ffingerly-io\u002Ffingerly-go\"\n\nclient := fingerly.New(os.Getenv(\"FINGERLY_SECRET_KEY\"))\nevent, err := client.Events.Get(ctx, \"01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\")",{"label":79,"lang":80,"code":81},"Java","java","import io.fingerly.server.FingerlyClient;\n\nFingerlyClient fingerly = FingerlyClient.builder()\n    .secretKey(System.getenv(\"FINGERLY_SECRET_KEY\"))\n    .build();\n\nEvent event = fingerly.events().get(\"01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\");",{"label":83,"lang":84,"code":85},".NET","csharp","using Fingerly;\n\nvar fingerly = new FingerlyClient(Environment.GetEnvironmentVariable(\"FINGERLY_SECRET_KEY\"));\nvar ev = await fingerly.Events.GetAsync(\"01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\");",{"label":87,"lang":88,"code":89},"PHP","php","\u003C?php\n\n$fingerly = new \\Fingerly\\Client(getenv('FINGERLY_SECRET_KEY'));\n$event = $fingerly->events->get('01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4');",{"label":91,"lang":92,"code":93},"Ruby","ruby","require \"fingerly\"\n\nfingerly = Fingerly::Client.new(secret_key: ENV.fetch(\"FINGERLY_SECRET_KEY\"))\nevent = fingerly.events.get(\"01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\")",{"label":95,"lang":96,"code":97},"Rust","rust","let fingerly = fingerly::Client::new(std::env::var(\"FINGERLY_SECRET_KEY\")?);\nlet event = fingerly.events().get(\"01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\").await?;",{"label":99,"lang":40,"code":100},"cURL","curl \"https:\u002F\u002Fus.api.fingerly.io\u002Fapi\u002Fv1\u002Fevents\u002F01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\" \\\n  -H \"x-api-key: $FINGERLY_SECRET_KEY\"",{"title":102,"blocks":103},"Decide",[104,106],{"type":11,"text":105},"Check the event belongs to this action and is recent, then use the level to choose what happens. Start by recording what you would have done before enforcing it.",{"type":27,"samples":107},[108,110,112,114,116,118,120,122,124],{"label":65,"lang":49,"code":109},"import { load, FingerlyAPIError } from '@fingerly\u002Fnode'\n\nconst fingerly = load({ secretKey: process.env.FINGERLY_SECRET_KEY! })\nconst MAX_AGE_MS = 2 * 60 * 1000\n\nexport async function decide(orderId: string, requestId: string) {\n  let event\n  try {\n    event = await fingerly.events.get(requestId)\n  } catch (error) {\n    if (error instanceof FingerlyAPIError && error.status === 404) return 'refuse'\n    throw error\n  }\n\n  if (event.tag !== 'checkout:' + orderId) return 'refuse'\n  if (Date.now() - Date.parse(event.occurred_at) > MAX_AGE_MS) return 'refuse'\n\n  if (event.suspect_level === 'high') return 'review'\n  if (event.suspect_level === 'medium') return 'challenge'\n  return 'allow'\n}",{"label":68,"lang":69,"code":111},"from datetime import datetime, timedelta, timezone\nfrom fingerly import Fingerly, FingerlyAPIError\n\nfingerly = Fingerly(secret_key=os.environ[\"FINGERLY_SECRET_KEY\"])\n\ndef decide(order_id: str, request_id: str) -> str:\n    try:\n        event = fingerly.events.get(request_id)\n    except FingerlyAPIError as error:\n        if error.status == 404:\n            return \"refuse\"\n        raise\n\n    if event.tag != f\"checkout:{order_id}\":\n        return \"refuse\"\n    if datetime.now(timezone.utc) - event.occurred_at > timedelta(minutes=2):\n        return \"refuse\"\n\n    if event.suspect_level == \"high\":\n        return \"review\"\n    if event.suspect_level == \"medium\":\n        return \"challenge\"\n    return \"allow\"",{"label":72,"lang":69,"code":113},"from datetime import datetime, timedelta, timezone\nfrom fingerly import AsyncFingerly, FingerlyAPIError\n\nfingerly = AsyncFingerly(secret_key=os.environ[\"FINGERLY_SECRET_KEY\"])\n\nasync def decide(order_id: str, request_id: str) -> str:\n    try:\n        event = await fingerly.events.get(request_id)\n    except FingerlyAPIError as error:\n        if error.status == 404:\n            return \"refuse\"\n        raise\n\n    if event.tag != f\"checkout:{order_id}\":\n        return \"refuse\"\n    if datetime.now(timezone.utc) - event.occurred_at > timedelta(minutes=2):\n        return \"refuse\"\n\n    return {\"high\": \"review\", \"medium\": \"challenge\"}.get(event.suspect_level, \"allow\")",{"label":75,"lang":76,"code":115},"func decide(ctx context.Context, orderID, requestID string) (string, error) {\n    event, err := client.Events.Get(ctx, requestID)\n    var apiErr *fingerly.APIError\n    if errors.As(err, &apiErr) && apiErr.Status == http.StatusNotFound {\n        return \"refuse\", nil\n    } else if err != nil {\n        return \"\", err\n    }\n\n    if event.Tag != \"checkout:\"+orderID || time.Since(event.OccurredAt) > 2*time.Minute {\n        return \"refuse\", nil\n    }\n\n    switch event.SuspectLevel {\n    case \"high\":\n        return \"review\", nil\n    case \"medium\":\n        return \"challenge\", nil\n    }\n    return \"allow\", nil\n}",{"label":79,"lang":80,"code":117},"public String decide(String orderId, String requestId) {\n    Event event;\n    try {\n        event = fingerly.events().get(requestId);\n    } catch (FingerlyApiException e) {\n        if (e.getStatus() == 404) return \"refuse\";\n        throw e;\n    }\n\n    if (!(\"checkout:\" + orderId).equals(event.getTag())) return \"refuse\";\n    if (event.getOccurredAt().isBefore(Instant.now().minus(Duration.ofMinutes(2)))) return \"refuse\";\n\n    return switch (String.valueOf(event.getSuspectLevel())) {\n        case \"high\" -> \"review\";\n        case \"medium\" -> \"challenge\";\n        default -> \"allow\";\n    };\n}",{"label":83,"lang":84,"code":119},"public async Task\u003Cstring> DecideAsync(string orderId, string requestId, CancellationToken ct)\n{\n    Event ev;\n    try\n    {\n        ev = await _fingerly.Events.GetAsync(requestId, ct);\n    }\n    catch (FingerlyApiException e) when (e.Status == 404)\n    {\n        return \"refuse\";\n    }\n\n    if (ev.Tag != $\"checkout:{orderId}\") return \"refuse\";\n    if (DateTimeOffset.UtcNow - ev.OccurredAt > TimeSpan.FromMinutes(2)) return \"refuse\";\n\n    return ev.SuspectLevel switch\n    {\n        \"high\" => \"review\",\n        \"medium\" => \"challenge\",\n        _ => \"allow\",\n    };\n}",{"label":87,"lang":88,"code":121},"\u003C?php\n\nuse Fingerly\\ApiException;\n\nfunction decide(string $orderId, string $requestId): string\n{\n    global $fingerly;\n\n    try {\n        $event = $fingerly->events->get($requestId);\n    } catch (ApiException $e) {\n        if ($e->getStatus() === 404) {\n            return 'refuse';\n        }\n        throw $e;\n    }\n\n    if ($event->tag !== \"checkout:{$orderId}\") {\n        return 'refuse';\n    }\n    if ($event->occurredAt \u003C new DateTimeImmutable('-2 minutes')) {\n        return 'refuse';\n    }\n\n    return match ($event->suspectLevel) {\n        'high' => 'review',\n        'medium' => 'challenge',\n        default => 'allow',\n    };\n}",{"label":91,"lang":92,"code":123},"def decide(order_id, request_id)\n  event = fingerly.events.get(request_id)\n\n  return \"refuse\" unless event.tag == \"checkout:#{order_id}\"\n  return \"refuse\" if event.occurred_at \u003C Time.now - 120\n\n  case event.suspect_level\n  when \"high\" then \"review\"\n  when \"medium\" then \"challenge\"\n  else \"allow\"\n  end\nrescue Fingerly::APIError => e\n  raise unless e.status == 404\n  \"refuse\"\nend",{"label":95,"lang":96,"code":125},"async fn decide(fingerly: &fingerly::Client, order_id: &str, request_id: &str) -> Result\u003CDecision, fingerly::Error> {\n    let event = match fingerly.events().get(request_id).await {\n        Ok(event) => event,\n        Err(fingerly::Error::Api { status: 404, .. }) => return Ok(Decision::Refuse),\n        Err(error) => return Err(error),\n    };\n\n    if event.tag.as_deref() != Some(&format!(\"checkout:{order_id}\")) {\n        return Ok(Decision::Refuse);\n    }\n    if chrono::Utc::now() - event.occurred_at > chrono::Duration::minutes(2) {\n        return Ok(Decision::Refuse);\n    }\n\n    Ok(match event.suspect_level {\n        Some(Level::High) => Decision::Review,\n        Some(Level::Medium) => Decision::Challenge,\n        _ => Decision::Allow,\n    })\n}",{"type":127,"level":128,"text":129,"id":130},"heading",2,"Try it","try-it",{"type":11,"text":132},"Open your page, log in, then open **Identification > Events** in the dashboard. Your identification is there with its visitor, score and signals. Now try a private window, a VPN or an automated browser, and watch the score change.",{"type":127,"level":128,"text":134,"id":135},"Go live","go-live",{"type":137,"items":138},"list",[139,140,141,142],"Create **production** public and secret keys with your real origins, and deploy them.","Production identifications cost $0.003 each. Add funds or turn on auto top-up in **Settings > Billing**.","Review your [risk weights](\u002Fdocs\u002Frisk-weights) and threshold against staging traffic before you enforce decisions.","Add [webhooks](\u002Fdocs\u002Fwebhooks) if you want results pushed to you.",{"type":144,"columns":128,"cards":145},"cards",[146,151],{"title":147,"text":148,"href":149,"icon":150},"Server-side verification","The checks to make before you trust a result.","\u002Fdocs\u002Fserver-side-verification","shield",{"title":152,"text":153,"href":154,"icon":155},"Plan your integration","Where to identify, what to tag, what to decide.","\u002Fdocs\u002Fplanning-your-integration","route",[157,160,162,164,166,168,169],{"id":158,"text":17,"level":159},"step-create-an-account-and-choose-a-region",3,{"id":161,"text":22,"level":159},"step-create-a-public-key",{"id":163,"text":34,"level":159},"step-identify-a-visitor-in-the-browser",{"id":165,"text":56,"level":159},"step-create-a-secret-key-and-read-the-result-on-your-server",{"id":167,"text":102,"level":159},"step-decide",{"id":130,"text":129,"level":128},{"id":135,"text":134,"level":128},"2026-09-17T16:58:12.000Z",1789667797513]