[{"data":1,"prerenderedAt":1098},["ShallowReactive",2],{"docs-nav":3,"docs-\u002Fdocs\u002Fapi\u002Fconcepts":70},[4,10,14,19,23,26,29,33,36,40,43,47,50,54,58,62,66],{"title":5,"path":6,"order":7,"section":8,"group":9},"Concepts","\u002Fdocs\u002Fapi\u002Fconcepts",1,"api",null,{"title":11,"path":12,"order":7,"section":13,"group":9},"Introduction","\u002Fdocs","general",{"title":15,"path":16,"order":7,"section":17,"group":18},"Usage","\u002Fdocs\u002Fsdk\u002Fusage","sdk","Script",{"title":20,"path":21,"order":22,"section":8,"group":9},"\u002Fv1\u002Fme","\u002Fdocs\u002Fapi\u002Fme",2,{"title":24,"path":25,"order":22,"section":13,"group":9},"Getting started","\u002Fdocs\u002Fgetting-started",{"title":24,"path":27,"order":22,"section":17,"group":28},"\u002Fdocs\u002Fsdk\u002Fpackage","Package",{"title":30,"path":31,"order":32,"section":8,"group":9},"\u002Fv1\u002Fsubscribe","\u002Fdocs\u002Fapi\u002Fsubscribe",3,{"title":34,"path":35,"order":32,"section":17,"group":28},"Browser client","\u002Fdocs\u002Fsdk\u002Fpackage\u002Fclient",{"title":37,"path":38,"order":39,"section":8,"group":9},"\u002Fv1\u002Funsubscribe","\u002Fdocs\u002Fapi\u002Funsubscribe",4,{"title":41,"path":42,"order":39,"section":17,"group":28},"Server client","\u002Fdocs\u002Fsdk\u002Fpackage\u002Fserver",{"title":44,"path":45,"order":46,"section":8,"group":9},"\u002Fv1\u002Fevents","\u002Fdocs\u002Fapi\u002Fevents",5,{"title":48,"path":49,"order":46,"section":17,"group":28},"Types","\u002Fdocs\u002Fsdk\u002Fpackage\u002Ftypes",{"title":51,"path":52,"order":53,"section":8,"group":9},"\u002Fv1\u002Fsend","\u002Fdocs\u002Fapi\u002Fsend",6,{"title":55,"path":56,"order":57,"section":8,"group":9},"\u002Fv1\u002Fbroadcasts","\u002Fdocs\u002Fapi\u002Fbroadcasts",7,{"title":59,"path":60,"order":61,"section":8,"group":9},"\u002Fv1\u002Fgroups","\u002Fdocs\u002Fapi\u002Fgroups",8,{"title":63,"path":64,"order":65,"section":8,"group":9},"\u002Fv1\u002Fsubscribers","\u002Fdocs\u002Fapi\u002Fsubscribers",9,{"title":67,"path":68,"order":69,"section":8,"group":9},"Webhooks","\u002Fdocs\u002Fapi\u002Fwebhooks",10,{"id":71,"title":5,"apiModule":9,"body":72,"description":1090,"extension":1091,"generated":1092,"group":9,"meta":1093,"navigation":1094,"order":7,"path":6,"section":8,"seo":1095,"stem":1096,"__hash__":1097},"docs\u002Fdocs\u002Fapi\u002Fconcepts.md",{"type":73,"value":74,"toc":1079},"minimark",[75,80,89,96,101,104,310,314,324,329,336,346,376,382,386,406,412,415,421,431,448,463,467,811,815,822,907,925,929,940,943,949,955,959,1075],[76,77,79],"h1",{"id":78},"api-concepts","API concepts",[81,82,83,84,88],"p",{},"Base URL: ",[85,86,87],"code",{},"https:\u002F\u002Fapi.litepush.dev",". All endpoints accept and return JSON unless otherwise stated.",[81,90,91,92,95],{},"This section is a reference, not a tutorial. If you're integrating for the first time, start with ",[93,94,24],"a",{"href":25}," instead. Each endpoint path has its own page in the sidebar — this page covers the shared concepts they all build on.",[97,98,100],"h2",{"id":99},"glossary","Glossary",[81,102,103],{},"Quick glossary so the field names across the endpoint pages make sense.",[105,106,107,123],"table",{},[108,109,110],"thead",{},[111,112,113,117,120],"tr",{},[114,115,116],"th",{},"Term",[114,118,119],{},"What it is",[114,121,122],{},"Where it comes from",[124,125,126,141,158,178,199,219,235,258,271,291],"tbody",{},[111,127,128,135,138],{},[129,130,131],"td",{},[132,133,134],"strong",{},"Project",[129,136,137],{},"A namespace that owns subscribers, broadcasts, groups, and keys. One project per site\u002Fapp you push to.",[129,139,140],{},"You create it in the dashboard.",[111,142,143,148,155],{},[129,144,145],{},[132,146,147],{},"Project ID",[129,149,150,151,154],{},"Public identifier — looks like ",[85,152,153],{},"prj_01HXM...",". Safe to expose in browser code.",[129,156,157],{},"Dashboard → Project overview.",[111,159,160,165,175],{},[129,161,162],{},[132,163,164],{},"API key",[129,166,167,168,171,172],{},"Secret server-to-server token — looks like ",[85,169,170],{},"lpk_live_*",". ",[132,173,174],{},"Shown once at project creation; we only store its SHA-256 hash.",[129,176,177],{},"Dashboard → Project overview (or rotate via Settings).",[111,179,180,185,196],{},[129,181,182],{},[132,183,184],{},"VAPID public key",[129,186,187,188,191,192,195],{},"Public half of the keypair used to sign push messages. Safe to embed in your ",[85,189,190],{},"\u003Cscript>"," tag. ",[132,193,194],{},"Rotating the keypair (Settings → Rotate VAPID keys) invalidates every active subscription"," — subscribers must re-opt in with the new key.",[129,197,198],{},"Generated automatically per project.",[111,200,201,206,213],{},[129,202,203],{},[132,204,205],{},"Subscriber",[129,207,208,209,212],{},"One browser that opted in to your project's pushes. Identified internally by ",[85,210,211],{},"sub_*",".",[129,214,215,216,212],{},"Created when the SDK calls ",[85,217,218],{},"POST \u002Fv1\u002Fsubscribe",[111,220,221,226,229],{},[129,222,223],{},[132,224,225],{},"Endpoint URL",[129,227,228],{},"The push gateway URL the browser handed back when the user opted in (FCM \u002F Mozilla \u002F Apple). Stable per browser install.",[129,230,231,234],{},[85,232,233],{},"PushSubscription.endpoint"," from the Web Push API.",[111,236,237,242,249],{},[129,238,239],{},[132,240,241],{},"p256dh \u002F auth keys",[129,243,244,245,248],{},"Encryption material from the browser's ",[85,246,247],{},"PushSubscription",". Required to encrypt the push payload.",[129,250,251,254,255,212],{},[85,252,253],{},"PushSubscription.getKey('p256dh')"," \u002F ",[85,256,257],{},"getKey('auth')",[111,259,260,265,268],{},[129,261,262],{},[132,263,264],{},"external_id",[129,266,267],{},"Your own user ID, optionally attached to a subscriber. Lets you target one user across multiple devices.",[129,269,270],{},"Your application.",[111,272,273,278,284],{},[129,274,275],{},[132,276,277],{},"Broadcast",[129,279,280,281,212],{},"One send operation — fan-out to all\u002Fgroup\u002Fuser subscribers. Identified by ",[85,282,283],{},"bdc_*",[129,285,286,287,290],{},"Created by ",[85,288,289],{},"POST \u002Fv1\u002Fsend"," or the dashboard's broadcast form.",[111,292,293,298,304],{},[129,294,295],{},[132,296,297],{},"Group",[129,299,300,301,212],{},"A named segment within a project. A subscriber can be in any number of groups. Identified by ",[85,302,303],{},"grp_*",[129,305,306,307,212],{},"Created via dashboard or ",[85,308,309],{},"POST \u002Fv1\u002Fgroups",[97,311,313],{"id":312},"authentication","Authentication",[81,315,316,317,320,321,212],{},"LitePush uses two auth styles depending on whether the caller is your ",[132,318,319],{},"server"," or a ",[132,322,323],{},"browser",[325,326,328],"h3",{"id":327},"server-to-server-bearer-token","Server-to-server — Bearer token",[81,330,331,332,335],{},"Server endpoints (anything you call from your backend) require your API key in the ",[85,333,334],{},"Authorization"," header:",[337,338,343],"pre",{"className":339,"code":341,"language":342},[340],"language-text","Authorization: Bearer lpk_live_xxxxxxxx\n","text",[85,344,341],{"__ignoreMap":345},"",[81,347,348,349,354,355,359,360,365,366,359,371,212],{},"Endpoints requiring Bearer auth: ",[93,350,351],{"href":21},[85,352,353],{},"GET \u002Fv1\u002Fme",", ",[93,356,357],{"href":52},[85,358,289],{},", all ",[93,361,362],{"href":56},[85,363,364],{},"\u002Fv1\u002Fbroadcasts*"," (read, list, cancel), all ",[93,367,368],{"href":60},[85,369,370],{},"\u002Fv1\u002Fgroups\u002F*",[93,372,373],{"href":64},[85,374,375],{},"\u002Fv1\u002Fsubscribers\u002F*",[81,377,378,379,212],{},"A missing, malformed, or revoked key returns ",[85,380,381],{},"401 invalid_api_key",[325,383,385],{"id":384},"browser-project-query-parameter","Browser — Project query parameter",[81,387,388,389,354,393,354,397,401,402,405],{},"Three endpoints are called from a browser (the SDK and the service worker): ",[93,390,391],{"href":31},[85,392,30],{},[93,394,395],{"href":38},[85,396,37],{},[93,398,399],{"href":45},[85,400,44],{},". These can't carry your secret API key — anyone would see it in DevTools — so they identify the project by its ",[132,403,404],{},"public"," ID instead:",[337,407,410],{"className":408,"code":409,"language":342},[340],"?project=prj_xxxxxxxx\n",[85,411,409],{"__ignoreMap":345},[81,413,414],{},"Or as a header (the SDK uses the header automatically):",[337,416,419],{"className":417,"code":418,"language":342},[340],"X-LitePush-Project: prj_xxxxxxxx\n",[85,420,418],{"__ignoreMap":345},[81,422,423,426,427,430],{},[85,424,425],{},"http:\u002F\u002Flocalhost"," origins are ",[132,428,429],{},"not"," accepted by the API. If you're developing against the endpoint, tunnel your dev server through Cloudflare Tunnel or ngrok and register the tunnel's HTTPS hostname as your project's origin.",[81,432,433,434,437,438,440,441,443,444,447],{},"Project IDs are ",[132,435,436],{},"public by design"," — they ship in your client-side ",[85,439,190],{}," tag and are visible in DevTools, like a publishable key, and that's fine: they're meant to be public. The only real secret is your ",[132,442,164],{}," (the ",[85,445,446],{},"lpk_live_…"," Bearer token) — keep that server-side, and rotate it from the dashboard if it ever leaks.",[81,449,450,451,454,455,457,458,462],{},"If you have a use case for ",[132,452,453],{},"server-side authoritative subscribe"," (seeding subscribers from your backend rather than from a real browser) — there isn't a Bearer-authed equivalent of ",[85,456,30],{}," today. Email ",[93,459,461],{"href":460},"mailto:support@litepush.dev","support@litepush.dev"," and tell us your shape.",[97,464,466],{"id":465},"quick-reference","Quick reference",[105,468,469,485],{},[108,470,471],{},[111,472,473,476,479,482],{},[114,474,475],{},"Method",[114,477,478],{},"Path",[114,480,481],{},"Auth",[114,483,484],{},"Purpose",[124,486,487,505,522,539,558,579,596,614,632,649,666,685,702,720,738,756,776,794],{},[111,488,489,494,500,502],{},[129,490,491],{},[85,492,493],{},"POST",[129,495,496],{},[93,497,498],{"href":31},[85,499,30],{},[129,501,134],{},[129,503,504],{},"Register a browser's push subscription",[111,506,507,511,517,519],{},[129,508,509],{},[85,510,493],{},[129,512,513],{},[93,514,515],{"href":38},[85,516,37],{},[129,518,134],{},[129,520,521],{},"Soft-unsubscribe a browser",[111,523,524,528,534,536],{},[129,525,526],{},[85,527,493],{},[129,529,530],{},[93,531,532],{"href":45},[85,533,44],{},[129,535,134],{},[129,537,538],{},"Service-worker click\u002Fdismiss beacon (SDK-internal)",[111,540,541,546,552,555],{},[129,542,543],{},[85,544,545],{},"GET",[129,547,548],{},[93,549,550],{"href":21},[85,551,20],{},[129,553,554],{},"Bearer",[129,556,557],{},"Verify your API key, get project identity",[111,559,560,564,570,572],{},[129,561,562],{},[85,563,493],{},[129,565,566],{},[93,567,568],{"href":52},[85,569,51],{},[129,571,554],{},[129,573,574,575,578],{},"Trigger a broadcast (immediate, or scheduled via ",[85,576,577],{},"scheduled_at",")",[111,580,581,585,591,593],{},[129,582,583],{},[85,584,545],{},[129,586,587],{},[93,588,589],{"href":56},[85,590,55],{},[129,592,554],{},[129,594,595],{},"List the project's broadcasts (paginated)",[111,597,598,602,609,611],{},[129,599,600],{},[85,601,545],{},[129,603,604],{},[93,605,606],{"href":56},[85,607,608],{},"\u002Fv1\u002Fbroadcasts\u002F:id",[129,610,554],{},[129,612,613],{},"Read one broadcast — status + delivered\u002Ffailed counts",[111,615,616,621,627,629],{},[129,617,618],{},[85,619,620],{},"DELETE",[129,622,623],{},[93,624,625],{"href":56},[85,626,608],{},[129,628,554],{},[129,630,631],{},"Cancel a not-yet-fired scheduled broadcast",[111,633,634,638,644,646],{},[129,635,636],{},[85,637,545],{},[129,639,640],{},[93,641,642],{"href":60},[85,643,59],{},[129,645,554],{},[129,647,648],{},"List groups in this project",[111,650,651,655,661,663],{},[129,652,653],{},[85,654,493],{},[129,656,657],{},[93,658,659],{"href":60},[85,660,59],{},[129,662,554],{},[129,664,665],{},"Create a group",[111,667,668,673,680,682],{},[129,669,670],{},[85,671,672],{},"PATCH",[129,674,675],{},[93,676,677],{"href":60},[85,678,679],{},"\u002Fv1\u002Fgroups\u002F:id",[129,681,554],{},[129,683,684],{},"Rename a group or edit its description",[111,686,687,691,697,699],{},[129,688,689],{},[85,690,620],{},[129,692,693],{},[93,694,695],{"href":60},[85,696,679],{},[129,698,554],{},[129,700,701],{},"Delete a group (memberships cascade, subscribers stay)",[111,703,704,708,715,717],{},[129,705,706],{},[85,707,493],{},[129,709,710],{},[93,711,712],{"href":60},[85,713,714],{},"\u002Fv1\u002Fgroups\u002F:id\u002Fsubscribers",[129,716,554],{},[129,718,719],{},"Add subscribers to a group",[111,721,722,726,733,735],{},[129,723,724],{},[85,725,620],{},[129,727,728],{},[93,729,730],{"href":60},[85,731,732],{},"\u002Fv1\u002Fgroups\u002F:id\u002Fsubscribers\u002F:sid",[129,734,554],{},[129,736,737],{},"Remove a subscriber from a group",[111,739,740,744,751,753],{},[129,741,742],{},[85,743,620],{},[129,745,746],{},[93,747,748],{"href":64},[85,749,750],{},"\u002Fv1\u002Fsubscribers\u002Fby-endpoint",[129,752,554],{},[129,754,755],{},"GDPR erasure by push endpoint URL",[111,757,758,762,769,771],{},[129,759,760],{},[85,761,620],{},[129,763,764],{},[93,765,766],{"href":64},[85,767,768],{},"\u002Fv1\u002Fsubscribers\u002Fby-external-id\u002F:eid",[129,770,554],{},[129,772,773,774],{},"GDPR erasure by ",[85,775,264],{},[111,777,778,782,789,791],{},[129,779,780],{},[85,781,545],{},[129,783,784],{},[93,785,786],{"href":64},[85,787,788],{},"\u002Fv1\u002Fsubscribers\u002Fexport",[129,790,554],{},[129,792,793],{},"GDPR portability — download CSV",[111,795,796,800,805,808],{},[129,797,798],{},[85,799,545],{},[129,801,802],{},[85,803,804],{},"\u002Fhealth",[129,806,807],{},"None",[129,809,810],{},"Service health probe",[97,812,814],{"id":813},"error-format","Error format",[81,816,817,818,821],{},"Every error response uses the same envelope so your code can branch on ",[85,819,820],{},"error.code",":",[337,823,827],{"className":824,"code":825,"language":826,"meta":345,"style":345},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"error\": {\n    \"code\": \"monthly_push_limit_reached\",\n    \"message\": \"Project would exceed the hobby plan cap of 200,000 pushes\u002Fmonth. This broadcast would add ~5,000.\"\n  }\n}\n","json",[85,828,829,837,854,878,897,902],{"__ignoreMap":345},[830,831,833],"span",{"class":832,"line":7},"line",[830,834,836],{"class":835},"sMK4o","{\n",[830,838,839,842,846,849,851],{"class":832,"line":22},[830,840,841],{"class":835},"  \"",[830,843,845],{"class":844},"spNyl","error",[830,847,848],{"class":835},"\"",[830,850,821],{"class":835},[830,852,853],{"class":835}," {\n",[830,855,856,859,862,864,866,869,873,875],{"class":832,"line":32},[830,857,858],{"class":835},"    \"",[830,860,85],{"class":861},"sBMFI",[830,863,848],{"class":835},[830,865,821],{"class":835},[830,867,868],{"class":835}," \"",[830,870,872],{"class":871},"sfazB","monthly_push_limit_reached",[830,874,848],{"class":835},[830,876,877],{"class":835},",\n",[830,879,880,882,885,887,889,891,894],{"class":832,"line":39},[830,881,858],{"class":835},[830,883,884],{"class":861},"message",[830,886,848],{"class":835},[830,888,821],{"class":835},[830,890,868],{"class":835},[830,892,893],{"class":871},"Project would exceed the hobby plan cap of 200,000 pushes\u002Fmonth. This broadcast would add ~5,000.",[830,895,896],{"class":835},"\"\n",[830,898,899],{"class":832,"line":46},[830,900,901],{"class":835},"  }\n",[830,903,904],{"class":832,"line":53},[830,905,906],{"class":835},"}\n",[908,909,910,918],"ul",{},[911,912,913,917],"li",{},[132,914,915],{},[85,916,85],{}," is a stable machine-parsable identifier. Treat it as your contract.",[911,919,920,924],{},[132,921,922],{},[85,923,884],{}," is human-readable and may change between releases. Show it in dashboards \u002F debug pages, but don't string-match it in code.",[97,926,928],{"id":927},"rate-limits","Rate limits",[81,930,931,932,935,936,939],{},"Each project is capped at ",[132,933,934],{},"600 requests\u002Fminute"," across all ",[85,937,938],{},"\u002Fv1\u002F*"," endpoints.",[81,941,942],{},"On overage you get:",[337,944,947],{"className":945,"code":946,"language":342},[340],"HTTP\u002F1.1 429 Too Many Requests\nRetry-After: 60\n\n{ \"error\": { \"code\": \"rate_limited\", \"message\": \"Project is over its per-minute API quota.\" } }\n",[85,948,946],{"__ignoreMap":345},[81,950,951,952,954],{},"If you're consistently hitting this, you're almost certainly fan-outing from client-side code that should be batched into a server-side cron — email ",[93,953,461],{"href":460}," and we'll help architect a fix.",[97,956,958],{"id":957},"status-codes-used","Status codes used",[105,960,961,971],{},[108,962,963],{},[111,964,965,968],{},[114,966,967],{},"Code",[114,969,970],{},"Meaning",[124,972,973,983,993,1003,1013,1023,1035,1045,1055,1065],{},[111,974,975,980],{},[129,976,977],{},[85,978,979],{},"200",[129,981,982],{},"OK",[111,984,985,990],{},[129,986,987],{},[85,988,989],{},"201",[129,991,992],{},"Created (subscribe, group create)",[111,994,995,1000],{},[129,996,997],{},[85,998,999],{},"202",[129,1001,1002],{},"Accepted (send, events beacon) — work continues async",[111,1004,1005,1010],{},[129,1006,1007],{},[85,1008,1009],{},"400",[129,1011,1012],{},"Bad request — invalid body, missing required param",[111,1014,1015,1020],{},[129,1016,1017],{},[85,1018,1019],{},"401",[129,1021,1022],{},"Auth failure — missing \u002F invalid Bearer token",[111,1024,1025,1030],{},[129,1026,1027],{},[85,1028,1029],{},"403",[129,1031,1032,1033,578],{},"Plan-cap or permission failure (see ",[85,1034,820],{},[111,1036,1037,1042],{},[129,1038,1039],{},[85,1040,1041],{},"404",[129,1043,1044],{},"Resource not found (project, group, etc.)",[111,1046,1047,1052],{},[129,1048,1049],{},[85,1050,1051],{},"409",[129,1053,1054],{},"Conflict (e.g. group name taken)",[111,1056,1057,1062],{},[129,1058,1059],{},[85,1060,1061],{},"429",[129,1063,1064],{},"Rate limited",[111,1066,1067,1072],{},[129,1068,1069],{},[85,1070,1071],{},"5xx",[129,1073,1074],{},"Server-side issue — please retry with exponential backoff",[1076,1077,1078],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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":345,"searchDepth":22,"depth":22,"links":1080},[1081,1082,1086,1087,1088,1089],{"id":99,"depth":22,"text":100},{"id":312,"depth":22,"text":313,"children":1083},[1084,1085],{"id":327,"depth":32,"text":328},{"id":384,"depth":32,"text":385},{"id":465,"depth":22,"text":466},{"id":813,"depth":22,"text":814},{"id":927,"depth":22,"text":928},{"id":957,"depth":22,"text":958},"Base URL, the glossary behind every field name, the two auth styles, the shared error envelope, rate limits, and status codes.","md",false,{},true,{"title":5,"description":1090},"docs\u002Fapi\u002Fconcepts","Hsr6lsU5h5qTP5LO2lueIM8bAOy2OgPoNy-XK6TXbM4",1780560203629]