[{"data":1,"prerenderedAt":491},["ShallowReactive",2],{"navigation_docs":3,"-docs-best-practices":102,"-docs-best-practices-surround":486},[4,8,12,16,20,24,28,32,36,40,44,82,86,90,94,98],{"title":5,"path":6,"stem":7},"Overview","\u002Fdocs","docs\u002Findex",{"title":9,"path":10,"stem":11},"Quickstart","\u002Fdocs\u002Fquickstart","docs\u002F01.quickstart",{"title":13,"path":14,"stem":15},"Authentication and keys","\u002Fdocs\u002Fauthentication","docs\u002F02.authentication",{"title":17,"path":18,"stem":19},"Images","\u002Fdocs\u002Fimages","docs\u002F03.images",{"title":21,"path":22,"stem":23},"Texts","\u002Fdocs\u002Ftexts","docs\u002F04.texts",{"title":25,"path":26,"stem":27},"Migrating from SightEngine","\u002Fdocs\u002Fmigrating-from-sightengine","docs\u002F05.migrating-from-sightengine",{"title":29,"path":30,"stem":31},"Workflows","\u002Fdocs\u002Fworkflows","docs\u002F06.workflows",{"title":33,"path":34,"stem":35},"Responses and errors","\u002Fdocs\u002Fresponses-and-errors","docs\u002F07.responses-and-errors",{"title":37,"path":38,"stem":39},"Limits and quotas","\u002Fdocs\u002Flimits-and-quotas","docs\u002F08.limits-and-quotas",{"title":41,"path":42,"stem":43},"The journal and ref","\u002Fdocs\u002Fjournal-and-ref","docs\u002F09.journal-and-ref",{"title":45,"path":46,"stem":47,"children":48,"page":81},"Integration guides","\u002Fdocs\u002Fintegration-guides","docs\u002F10.integration-guides",[49,53,57,61,65,69,73,77],{"title":50,"path":51,"stem":52},"Node.js and TypeScript","\u002Fdocs\u002Fintegration-guides\u002Fnodejs","docs\u002F10.integration-guides\u002F1.nodejs",{"title":54,"path":55,"stem":56},"Python","\u002Fdocs\u002Fintegration-guides\u002Fpython","docs\u002F10.integration-guides\u002F2.python",{"title":58,"path":59,"stem":60},"PHP","\u002Fdocs\u002Fintegration-guides\u002Fphp","docs\u002F10.integration-guides\u002F3.php",{"title":62,"path":63,"stem":64},"Go","\u002Fdocs\u002Fintegration-guides\u002Fgo","docs\u002F10.integration-guides\u002F4.go",{"title":66,"path":67,"stem":68},"Supabase Edge Functions","\u002Fdocs\u002Fintegration-guides\u002Fsupabase-edge-functions","docs\u002F10.integration-guides\u002F5.supabase-edge-functions",{"title":70,"path":71,"stem":72},"Cloudflare Workers","\u002Fdocs\u002Fintegration-guides\u002Fcloudflare-workers","docs\u002F10.integration-guides\u002F6.cloudflare-workers",{"title":74,"path":75,"stem":76},"Nuxt and Next","\u002Fdocs\u002Fintegration-guides\u002Fnuxt-and-next","docs\u002F10.integration-guides\u002F7.nuxt-and-next",{"title":78,"path":79,"stem":80},"Moderate an AI agent","\u002Fdocs\u002Fintegration-guides\u002Fmoderate-ai-agents","docs\u002F10.integration-guides\u002F8.moderate-ai-agents",false,{"title":83,"path":84,"stem":85},"AI coding agents","\u002Fdocs\u002Fai-agents","docs\u002F11.ai-agents",{"title":87,"path":88,"stem":89},"Best practices","\u002Fdocs\u002Fbest-practices","docs\u002F12.best-practices",{"title":91,"path":92,"stem":93},"Privacy and data","\u002Fdocs\u002Fprivacy-and-data","docs\u002F13.privacy-and-data",{"title":95,"path":96,"stem":97},"API reference","\u002Fdocs\u002Fapi-reference","docs\u002F14.api-reference",{"title":99,"path":100,"stem":101},"Changelog","\u002Fdocs\u002Fchangelog","docs\u002F15.changelog",{"id":103,"title":87,"body":104,"description":479,"extension":480,"links":481,"meta":482,"navigation":483,"path":88,"seo":484,"stem":89,"__hash__":485},"docs\u002Fdocs\u002F12.best-practices.md",{"type":105,"value":106,"toc":469},"minimark",[107,112,125,129,145,180,184,210,214,229,377,387,391,411,415,436,440,451,455,465],[108,109,111],"h2",{"id":110},"call-the-api-from-your-servers-only","Call the API from your servers only",[113,114,115,116,120,121,124],"p",{},"A key is a secret. Call openshield from your backend, and give your front end the decision, not the key. The API answers a request that carries a browser ",[117,118,119],"code",{},"Origin"," header with ",[117,122,123],{},"403 forbidden",", so a call from a browser fails anyway, and a key sent to a browser is exposed.",[108,126,128],{"id":127},"fail-closed","Fail closed",[113,130,131,132,135,136,139,140,144],{},"A check can fail: a timeout, a network error, a ",[117,133,134],{},"500"," or a ",[117,137,138],{},"503",". Decide what happens to the content when it does, and make the safe choice the default: ",[141,142,143],"strong",{},"hold it"," until a person has looked at it, or until a retry has succeeded. Publishing content that was never checked is the case a moderation service exists to prevent.",[146,147,148,152,166],"ul",{},[149,150,151],"li",{},"Set a timeout on the call, 10 seconds is a good start.",[149,153,154,155,158,159,161,162,165],{},"Retry ",[117,156,157],{},"429"," and ",[117,160,138],{}," after the ",[117,163,164],{},"Retry-After"," header, at most twice.",[149,167,168,169,172,173,158,176,179],{},"Do not retry ",[117,170,171],{},"400",", ",[117,174,175],{},"413",[117,177,178],{},"415",": the content cannot be accepted as it is. Refuse it.",[108,181,183],{"id":182},"act-on-the-three-outcomes","Act on the three outcomes",[146,185,186,194,202],{},[149,187,188,193],{},[141,189,190],{},[117,191,192],{},"accept",": publish.",[149,195,196,201],{},[141,197,198],{},[117,199,200],{},"review",": hide the content from other users until a person approves it. If your product has no moderation queue, add the smallest one that fits: a flag on the row and a screen to list the flagged ones.",[149,203,204,209],{},[141,205,206],{},[117,207,208],{},"reject",": refuse the content and tell the user why.",[108,211,213],{"id":212},"choose-your-messages-by-category-id","Choose your messages by category id",[113,215,216,217,220,221,224,225,228],{},"Each reason has an ",[117,218,219],{},"id",", such as ",[117,222,223],{},"nudity"," or ",[117,226,227],{},"pii_email",". It is stable. Map it to messages you write, in your user's language:",[230,231,236],"pre",{"className":232,"code":233,"language":234,"meta":235,"style":235},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const messages: Record\u003Cstring, string> = {\n  nudity: \"This image can't be used here.\",\n  pii_email: \"Please don't share email addresses in comments.\",\n}\nconst message = messages[decision.rejectReasons[0]?.id ?? \"\"] ?? \"This content can't be published.\"\n","ts","",[117,237,238,280,302,319,325],{"__ignoreMap":235},[239,240,243,247,251,255,259,262,265,268,271,274,277],"span",{"class":241,"line":242},"line",1,[239,244,246],{"class":245},"spNyl","const",[239,248,250],{"class":249},"sTEyZ"," messages",[239,252,254],{"class":253},"sMK4o",":",[239,256,258],{"class":257},"sBMFI"," Record",[239,260,261],{"class":253},"\u003C",[239,263,264],{"class":257},"string",[239,266,267],{"class":253},",",[239,269,270],{"class":257}," string",[239,272,273],{"class":253},">",[239,275,276],{"class":253}," =",[239,278,279],{"class":253}," {\n",[239,281,283,287,289,292,296,299],{"class":241,"line":282},2,[239,284,286],{"class":285},"swJcz","  nudity",[239,288,254],{"class":253},[239,290,291],{"class":253}," \"",[239,293,295],{"class":294},"sfazB","This image can't be used here.",[239,297,298],{"class":253},"\"",[239,300,301],{"class":253},",\n",[239,303,305,308,310,312,315,317],{"class":241,"line":304},3,[239,306,307],{"class":285},"  pii_email",[239,309,254],{"class":253},[239,311,291],{"class":253},[239,313,314],{"class":294},"Please don't share email addresses in comments.",[239,316,298],{"class":253},[239,318,301],{"class":253},[239,320,322],{"class":241,"line":321},4,[239,323,324],{"class":253},"}\n",[239,326,328,330,333,336,339,342,345,349,352,355,358,361,364,367,369,371,374],{"class":241,"line":327},5,[239,329,246],{"class":245},[239,331,332],{"class":249}," message ",[239,334,335],{"class":253},"=",[239,337,338],{"class":249}," messages[decision",[239,340,341],{"class":253},".",[239,343,344],{"class":249},"rejectReasons[",[239,346,348],{"class":347},"sbssI","0",[239,350,351],{"class":249},"]",[239,353,354],{"class":253},"?.",[239,356,357],{"class":249},"id ",[239,359,360],{"class":253},"??",[239,362,363],{"class":253}," \"\"",[239,365,366],{"class":249},"] ",[239,368,360],{"class":253},[239,370,291],{"class":253},[239,372,373],{"class":294},"This content can't be published.",[239,375,376],{"class":253},"\"\n",[113,378,379,380,158,383,386],{},"The English ",[117,381,382],{},"text",[117,384,385],{},"message"," of a reason are fallbacks. Do not match on them: they can be reworded.",[108,388,390],{"id":389},"send-what-you-need-to-send","Send what you need to send",[146,392,393,399,405],{},[149,394,395,398],{},[141,396,397],{},"Downscale large photos"," to about 1,280 pixels on the longer side: the upload is smaller and faster. Keep the full size of screenshots, where small text or a QR code matters.",[149,400,401,404],{},[141,402,403],{},"Send each image once."," Cache the decision with your own record of the content.",[149,406,407,410],{},[141,408,409],{},"Check what other people see."," Posts, comments, messages, profile pictures, display names, bios, listings and reviews: anything a user can publish.",[108,412,414],{"id":413},"keep-a-trail","Keep a trail",[146,416,417,429],{},[149,418,419,420,423,424,428],{},"Send a ",[117,421,422],{},"ref",", your own opaque id for the content, so you can find its decision in the ",[425,426,427],"a",{"href":42},"journal",". Do not put personal data in it.",[149,430,431,432,435],{},"Log the ",[117,433,434],{},"request_id"," of every answer next to your own record.",[108,437,439],{"id":438},"keys-and-workflows","Keys and workflows",[146,441,442,445,448],{},[149,443,444],{},"Give each service its own key, with the narrowest scope and workflows it needs.",[149,446,447],{},"Rotate keys with a grace delay, so a deploy never has a gap.",[149,449,450],{},"Use one workflow per product surface when the rules differ: stricter for a children's section, more lenient for a private chat.",[108,452,454],{"id":453},"test-without-the-network","Test without the network",[113,456,457,458,172,460,172,462,464],{},"Stub the HTTP call in your tests, and assert what your code does for ",[117,459,192],{},[117,461,200],{},[117,463,208],{}," and each kind of error. Do not call the real API from a test suite: it counts against your quota.",[466,467,468],"style",{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":235,"searchDepth":282,"depth":282,"links":470},[471,472,473,474,475,476,477,478],{"id":110,"depth":282,"text":111},{"id":127,"depth":282,"text":128},{"id":182,"depth":282,"text":183},{"id":212,"depth":282,"text":213},{"id":389,"depth":282,"text":390},{"id":413,"depth":282,"text":414},{"id":438,"depth":282,"text":439},{"id":453,"depth":282,"text":454},"Server-side only, fail closed, downscale images, and show users messages you choose by category id.","md",null,{},true,{"title":87,"description":479},"58ytn1iuwTklZgnrSmg9-N8M07LxRJIUzwL2jlVrHP0",[487,489],{"title":83,"path":84,"stem":85,"description":488,"children":-1},"Hand the integration to your coding agent with a ready prompt, llms.txt and the OpenAPI document.",{"title":91,"path":92,"stem":93,"description":490,"children":-1},"What openshield stores and what it never stores, where, and for how long.",1792436179912]