[{"data":1,"prerenderedAt":977},["ShallowReactive",2],{"/docs/scripts/free-phone/exports/server-exports":3,"/docs/scripts/free-phone/exports/server-exports-surround":965,"/docs/scripts/free-phone/exports/server-exports-implemented-suggestions":976},{"id":4,"title":5,"body":6,"description":956,"extension":957,"head":958,"implementedSuggestions":958,"meta":959,"navigation":234,"path":960,"schemaOrg":958,"seo":961,"sitemap":962,"stem":963,"__hash__":964},"docs/1.docs/scripts/4.free-phone/3.exports/2.server-exports.md","Server Exports",{"type":7,"value":8,"toc":945},"minimark",[9,17,20,33,37,44,49,52,58,115,283,289,318,448,454,481,558,564,591,716,719,725,759,846,852,865,941],[10,11,12],"p",{},[13,14,16],"a",{"href":15},"/docs/scripts/free-phone/exports","All exports",[10,18,19],{},"This page documents the supported public integration calls. Client exports must run in a client\nscript and server exports must run in a server script. Internal NUI callbacks and net events are not\npublic APIs.",[21,22,25],"callout",{"color":23,"icon":24},"success","i-lucide-shield-check",[10,26,27,28,32],{},"Keep the resource folder named ",[29,30,31],"code",{},"sky_phone",". Validate every returned success value and keep money,\npermissions, ownership, and other authoritative changes on the server.",[34,35,5],"h2",{"id":36},"server-exports",[10,38,39,40,43],{},"Server exports handle custom-app policies, billing, and CrewLink integrations. Do not proxy these\nexports through an unvalidated ",[29,41,42],{},"RegisterNetEvent",".",[45,46,48],"h3",{"id":47},"custom-app-policies","Custom-app policies",[10,50,51],{},"Client definitions and server policies are separate. A client definition cannot grant itself\nserver-side permissions.",[45,53,55],{"id":54},"addcustomapppolicydefinition",[29,56,57],{},"AddCustomAppPolicy(definition)",[59,60,61,69,106],"ul",{},[62,63,64,68],"li",{},[65,66,67],"strong",{},"Purpose",": Registers a server policy owned by the calling resource.",[62,70,71,74,75],{},[65,72,73],{},"Arguments",":\n",[59,76,77],{},[62,78,79,82,83,86,87],{},[29,80,81],{},"definition"," (",[29,84,85],{},"table","): Policy definition.\n",[59,88,89,98],{},[62,90,91,82,94,97],{},[29,92,93],{},"id",[29,95,96],{},"string","): Matching custom-app ID. Required.",[62,99,100,82,103,105],{},[29,101,102],{},"permissions",[29,104,85],{},"): Server-approved permissions.",[62,107,108,111,112,43],{},[65,109,110],{},"Returns",": ",[29,113,114],{},"boolean, string?",[116,117,123],"pre",{"className":118,"code":119,"filename":120,"language":121,"meta":122,"style":122},"language-lua shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","local registered, error_code = exports[\"sky_phone\"]:AddCustomAppPolicy({\n    id = \"dispatch-board\",\n    permissions = {\n        \"device.storage\",\n        \"notifications\",\n    },\n})\n\nif not registered then\n    error((\"[my_dispatch] Policy registration failed: %s\"):format(error_code))\nend\n","server.lua","lua","",[29,124,125,162,181,192,205,217,223,229,236,252,277],{"__ignoreMap":122},[126,127,130,134,138,141,144,147,150,152,155,159],"span",{"class":128,"line":129},"line",1,[126,131,133],{"class":132},"sMK4o","local",[126,135,137],{"class":136},"sTEyZ"," registered, error_code ",[126,139,140],{"class":132},"=",[126,142,143],{"class":136}," exports[",[126,145,146],{"class":132},"\"",[126,148,31],{"class":149},"sfazB",[126,151,146],{"class":132},[126,153,154],{"class":136},"]:",[126,156,158],{"class":157},"s2Zo4","AddCustomAppPolicy",[126,160,161],{"class":136},"({\n",[126,163,165,168,170,173,176,178],{"class":128,"line":164},2,[126,166,167],{"class":136},"    id ",[126,169,140],{"class":132},[126,171,172],{"class":132}," \"",[126,174,175],{"class":149},"dispatch-board",[126,177,146],{"class":132},[126,179,180],{"class":136},",\n",[126,182,184,187,189],{"class":128,"line":183},3,[126,185,186],{"class":136},"    permissions ",[126,188,140],{"class":132},[126,190,191],{"class":136}," {\n",[126,193,195,198,201,203],{"class":128,"line":194},4,[126,196,197],{"class":132},"        \"",[126,199,200],{"class":149},"device.storage",[126,202,146],{"class":132},[126,204,180],{"class":136},[126,206,208,210,213,215],{"class":128,"line":207},5,[126,209,197],{"class":132},[126,211,212],{"class":149},"notifications",[126,214,146],{"class":132},[126,216,180],{"class":136},[126,218,220],{"class":128,"line":219},6,[126,221,222],{"class":136},"    },\n",[126,224,226],{"class":128,"line":225},7,[126,227,228],{"class":136},"})\n",[126,230,232],{"class":128,"line":231},8,[126,233,235],{"emptyLinePlaceholder":234},true,"\n",[126,237,239,243,246,249],{"class":128,"line":238},9,[126,240,242],{"class":241},"s7zQu","if",[126,244,245],{"class":132}," not",[126,247,248],{"class":136}," registered ",[126,250,251],{"class":241},"then\n",[126,253,255,258,261,263,266,268,271,274],{"class":128,"line":254},10,[126,256,257],{"class":157},"    error",[126,259,260],{"class":136},"((",[126,262,146],{"class":132},[126,264,265],{"class":149},"[my_dispatch] Policy registration failed: %s",[126,267,146],{"class":132},[126,269,270],{"class":136},"):",[126,272,273],{"class":157},"format",[126,275,276],{"class":136},"(error_code))\n",[126,278,280],{"class":128,"line":279},11,[126,281,282],{"class":241},"end\n",[45,284,286],{"id":285},"updatecustomapppolicydefinition",[29,287,288],{},"UpdateCustomAppPolicy(definition)",[59,290,291,296,312],{},[62,292,293,295],{},[65,294,67],{},": Replaces the complete policy owned by the calling resource.",[62,297,298,74,300],{},[65,299,73],{},[59,301,302],{},[62,303,304,82,306,308,309,311],{},[29,305,81],{},[29,307,85],{},"): Full policy including ",[29,310,93],{}," and every allowed permission.",[62,313,314,111,316,43],{},[65,315,110],{},[29,317,114],{},[116,319,321],{"className":118,"code":320,"filename":120,"language":121,"meta":122,"style":122},"local updated, error_code = exports[\"sky_phone\"]:UpdateCustomAppPolicy({\n    id = \"dispatch-board\",\n    permissions = {\n        \"device.storage\",\n        \"notifications\",\n        \"notifications.critical\",\n    },\n})\n\nif not updated then\n    print((\"[my_dispatch] Policy update failed: %s\"):format(error_code))\nend\n",[29,322,323,347,361,369,379,389,400,404,408,412,423,443],{"__ignoreMap":122},[126,324,325,327,330,332,334,336,338,340,342,345],{"class":128,"line":129},[126,326,133],{"class":132},[126,328,329],{"class":136}," updated, error_code ",[126,331,140],{"class":132},[126,333,143],{"class":136},[126,335,146],{"class":132},[126,337,31],{"class":149},[126,339,146],{"class":132},[126,341,154],{"class":136},[126,343,344],{"class":157},"UpdateCustomAppPolicy",[126,346,161],{"class":136},[126,348,349,351,353,355,357,359],{"class":128,"line":164},[126,350,167],{"class":136},[126,352,140],{"class":132},[126,354,172],{"class":132},[126,356,175],{"class":149},[126,358,146],{"class":132},[126,360,180],{"class":136},[126,362,363,365,367],{"class":128,"line":183},[126,364,186],{"class":136},[126,366,140],{"class":132},[126,368,191],{"class":136},[126,370,371,373,375,377],{"class":128,"line":194},[126,372,197],{"class":132},[126,374,200],{"class":149},[126,376,146],{"class":132},[126,378,180],{"class":136},[126,380,381,383,385,387],{"class":128,"line":207},[126,382,197],{"class":132},[126,384,212],{"class":149},[126,386,146],{"class":132},[126,388,180],{"class":136},[126,390,391,393,396,398],{"class":128,"line":219},[126,392,197],{"class":132},[126,394,395],{"class":149},"notifications.critical",[126,397,146],{"class":132},[126,399,180],{"class":136},[126,401,402],{"class":128,"line":225},[126,403,222],{"class":136},[126,405,406],{"class":128,"line":231},[126,407,228],{"class":136},[126,409,410],{"class":128,"line":238},[126,411,235],{"emptyLinePlaceholder":234},[126,413,414,416,418,421],{"class":128,"line":254},[126,415,242],{"class":241},[126,417,245],{"class":132},[126,419,420],{"class":136}," updated ",[126,422,251],{"class":241},[126,424,425,428,430,432,435,437,439,441],{"class":128,"line":279},[126,426,427],{"class":157},"    print",[126,429,260],{"class":136},[126,431,146],{"class":132},[126,433,434],{"class":149},"[my_dispatch] Policy update failed: %s",[126,436,146],{"class":132},[126,438,270],{"class":136},[126,440,273],{"class":157},[126,442,276],{"class":136},[126,444,446],{"class":128,"line":445},12,[126,447,282],{"class":241},[45,449,451],{"id":450},"removecustomapppolicyappid",[29,452,453],{},"RemoveCustomAppPolicy(appId)",[59,455,456,461,475],{},[62,457,458,460],{},[65,459,67],{},": Removes a policy owned by the calling resource.",[62,462,463,74,465],{},[65,464,73],{},[59,466,467],{},[62,468,469,82,472,474],{},[29,470,471],{},"appId",[29,473,96],{},"): Registered app ID.",[62,476,477,111,479,43],{},[65,478,110],{},[29,480,114],{},[116,482,484],{"className":118,"code":483,"filename":120,"language":121,"meta":122,"style":122},"local removed, error_code = exports[\"sky_phone\"]:RemoveCustomAppPolicy(\"dispatch-board\")\n\nif not removed then\n    print((\"[my_dispatch] Policy removal failed: %s\"):format(error_code))\nend\n",[29,485,486,520,524,535,554],{"__ignoreMap":122},[126,487,488,490,493,495,497,499,501,503,505,508,511,513,515,517],{"class":128,"line":129},[126,489,133],{"class":132},[126,491,492],{"class":136}," removed, error_code ",[126,494,140],{"class":132},[126,496,143],{"class":136},[126,498,146],{"class":132},[126,500,31],{"class":149},[126,502,146],{"class":132},[126,504,154],{"class":136},[126,506,507],{"class":157},"RemoveCustomAppPolicy",[126,509,510],{"class":136},"(",[126,512,146],{"class":132},[126,514,175],{"class":149},[126,516,146],{"class":132},[126,518,519],{"class":136},")\n",[126,521,522],{"class":128,"line":164},[126,523,235],{"emptyLinePlaceholder":234},[126,525,526,528,530,533],{"class":128,"line":183},[126,527,242],{"class":241},[126,529,245],{"class":132},[126,531,532],{"class":136}," removed ",[126,534,251],{"class":241},[126,536,537,539,541,543,546,548,550,552],{"class":128,"line":194},[126,538,427],{"class":157},[126,540,260],{"class":136},[126,542,146],{"class":132},[126,544,545],{"class":149},"[my_dispatch] Policy removal failed: %s",[126,547,146],{"class":132},[126,549,270],{"class":136},[126,551,273],{"class":157},[126,553,276],{"class":136},[126,555,556],{"class":128,"line":207},[126,557,282],{"class":241},[45,559,561],{"id":560},"getcustomapppolicyappid",[29,562,563],{},"GetCustomAppPolicy(appId)",[59,565,566,571,584],{},[62,567,568,570],{},[65,569,67],{},": Reads the normalized server policy for an app.",[62,572,573,74,575],{},[65,574,73],{},[59,576,577],{},[62,578,579,82,581,583],{},[29,580,471],{},[29,582,96],{},"): App ID.",[62,585,586,111,588,43],{},[65,587,110],{},[29,589,590],{},"table | nil",[116,592,594],{"className":118,"code":593,"filename":120,"language":121,"meta":122,"style":122},"local policy = exports[\"sky_phone\"]:GetCustomAppPolicy(\"dispatch-board\")\n\nif policy then\n    print(\"Owner\", policy.ownerResource)\n    for _, permission in ipairs(policy.permissions or {}) do\n        print(\"Permission\", permission)\n    end\nend\n",[29,595,596,628,632,640,662,690,707,712],{"__ignoreMap":122},[126,597,598,600,603,605,607,609,611,613,615,618,620,622,624,626],{"class":128,"line":129},[126,599,133],{"class":132},[126,601,602],{"class":136}," policy ",[126,604,140],{"class":132},[126,606,143],{"class":136},[126,608,146],{"class":132},[126,610,31],{"class":149},[126,612,146],{"class":132},[126,614,154],{"class":136},[126,616,617],{"class":157},"GetCustomAppPolicy",[126,619,510],{"class":136},[126,621,146],{"class":132},[126,623,175],{"class":149},[126,625,146],{"class":132},[126,627,519],{"class":136},[126,629,630],{"class":128,"line":164},[126,631,235],{"emptyLinePlaceholder":234},[126,633,634,636,638],{"class":128,"line":183},[126,635,242],{"class":241},[126,637,602],{"class":136},[126,639,251],{"class":241},[126,641,642,644,646,648,651,653,656,660],{"class":128,"line":194},[126,643,427],{"class":157},[126,645,510],{"class":136},[126,647,146],{"class":132},[126,649,650],{"class":149},"Owner",[126,652,146],{"class":132},[126,654,655],{"class":136},", policy.",[126,657,659],{"class":658},"sBMFI","ownerResource",[126,661,519],{"class":136},[126,663,664,667,670,673,676,679,681,684,687],{"class":128,"line":207},[126,665,666],{"class":241},"    for",[126,668,669],{"class":136}," _, permission ",[126,671,672],{"class":241},"in",[126,674,675],{"class":157}," ipairs",[126,677,678],{"class":136},"(policy.",[126,680,102],{"class":658},[126,682,683],{"class":132}," or",[126,685,686],{"class":136}," {}) ",[126,688,689],{"class":241},"do\n",[126,691,692,695,697,699,702,704],{"class":128,"line":219},[126,693,694],{"class":157},"        print",[126,696,510],{"class":136},[126,698,146],{"class":132},[126,700,701],{"class":149},"Permission",[126,703,146],{"class":132},[126,705,706],{"class":136},", permission)\n",[126,708,709],{"class":128,"line":225},[126,710,711],{"class":241},"    end\n",[126,713,714],{"class":128,"line":231},[126,715,282],{"class":241},[10,717,718],{},"Treat the returned table as a snapshot. Mutating it does not update the registered policy.",[45,720,722],{"id":721},"hascustomapppermissionappid-permission",[29,723,724],{},"HasCustomAppPermission(appId, permission)",[59,726,727,732,752],{},[62,728,729,731],{},[65,730,67],{},": Checks whether a registered policy contains one permission.",[62,733,734,74,736],{},[65,735,73],{},[59,737,738,744],{},[62,739,740,82,742,583],{},[29,741,471],{},[29,743,96],{},[62,745,746,82,749,751],{},[29,747,748],{},"permission",[29,750,96],{},"): Supported permission name.",[62,753,754,111,756,43],{},[65,755,110],{},[29,757,758],{},"boolean",[116,760,762],{"className":118,"code":761,"filename":120,"language":121,"meta":122,"style":122},"local can_store = exports[\"sky_phone\"]:HasCustomAppPermission(\n    \"dispatch-board\",\n    \"device.storage\"\n)\n\nif not can_store then\n    print(\"Dispatch Board has no storage permission\")\nend\n",[29,763,764,789,800,809,813,817,827,842],{"__ignoreMap":122},[126,765,766,768,771,773,775,777,779,781,783,786],{"class":128,"line":129},[126,767,133],{"class":132},[126,769,770],{"class":136}," can_store ",[126,772,140],{"class":132},[126,774,143],{"class":136},[126,776,146],{"class":132},[126,778,31],{"class":149},[126,780,146],{"class":132},[126,782,154],{"class":136},[126,784,785],{"class":157},"HasCustomAppPermission",[126,787,788],{"class":136},"(\n",[126,790,791,794,796,798],{"class":128,"line":164},[126,792,793],{"class":132},"    \"",[126,795,175],{"class":149},[126,797,146],{"class":132},[126,799,180],{"class":136},[126,801,802,804,806],{"class":128,"line":183},[126,803,793],{"class":132},[126,805,200],{"class":149},[126,807,808],{"class":132},"\"\n",[126,810,811],{"class":128,"line":194},[126,812,519],{"class":136},[126,814,815],{"class":128,"line":207},[126,816,235],{"emptyLinePlaceholder":234},[126,818,819,821,823,825],{"class":128,"line":219},[126,820,242],{"class":241},[126,822,245],{"class":132},[126,824,770],{"class":136},[126,826,251],{"class":241},[126,828,829,831,833,835,838,840],{"class":128,"line":225},[126,830,427],{"class":157},[126,832,510],{"class":136},[126,834,146],{"class":132},[126,836,837],{"class":149},"Dispatch Board has no storage permission",[126,839,146],{"class":132},[126,841,519],{"class":136},[126,843,844],{"class":128,"line":231},[126,845,282],{"class":241},[45,847,849],{"id":848},"getcustomappcapabilities",[29,850,851],{},"GetCustomAppCapabilities()",[59,853,854,859],{},[62,855,856,858],{},[65,857,67],{},": Reads the server-side custom-app ABI and policy capabilities.",[62,860,861,111,863,43],{},[65,862,110],{},[29,864,85],{},[116,866,868],{"className":118,"code":867,"filename":120,"language":121,"meta":122,"style":122},"local capabilities = exports[\"sky_phone\"]:GetCustomAppCapabilities()\n\nprint(\"External apps enabled\", capabilities.enabled)\nprint(\"ABI\", capabilities.abiVersion)\n",[29,869,870,895,899,921],{"__ignoreMap":122},[126,871,872,874,877,879,881,883,885,887,889,892],{"class":128,"line":129},[126,873,133],{"class":132},[126,875,876],{"class":136}," capabilities ",[126,878,140],{"class":132},[126,880,143],{"class":136},[126,882,146],{"class":132},[126,884,31],{"class":149},[126,886,146],{"class":132},[126,888,154],{"class":136},[126,890,891],{"class":157},"GetCustomAppCapabilities",[126,893,894],{"class":136},"()\n",[126,896,897],{"class":128,"line":164},[126,898,235],{"emptyLinePlaceholder":234},[126,900,901,904,906,908,911,913,916,919],{"class":128,"line":183},[126,902,903],{"class":157},"print",[126,905,510],{"class":136},[126,907,146],{"class":132},[126,909,910],{"class":149},"External apps enabled",[126,912,146],{"class":132},[126,914,915],{"class":136},", capabilities.",[126,917,918],{"class":658},"enabled",[126,920,519],{"class":136},[126,922,923,925,927,929,932,934,936,939],{"class":128,"line":194},[126,924,903],{"class":157},[126,926,510],{"class":136},[126,928,146],{"class":132},[126,930,931],{"class":149},"ABI",[126,933,146],{"class":132},[126,935,915],{"class":136},[126,937,938],{"class":658},"abiVersion",[126,940,519],{"class":136},[942,943,944],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}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);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":122,"searchDepth":129,"depth":164,"links":946},[947],{"id":36,"depth":164,"text":5,"children":948},[949,950,951,952,953,954,955],{"id":47,"depth":183,"text":48},{"id":54,"depth":183,"text":57},{"id":285,"depth":183,"text":288},{"id":450,"depth":183,"text":453},{"id":560,"depth":183,"text":563},{"id":721,"depth":183,"text":724},{"id":848,"depth":183,"text":851},"Server Exports for the Free Phone custom-app, billing and CrewLink integrations.","md",null,{},"/docs/scripts/free-phone/exports/server-exports",{"title":5,"description":956},{"loc":960},"1.docs/scripts/4.free-phone/3.exports/2.server-exports","EJLaNsQ6Cls-J0Zo908hzslHpZzlrCZ8PNI4fsX1IpU",[966,971],{"title":967,"path":968,"stem":969,"description":970,"children":-1},"Client Exports","/docs/scripts/free-phone/exports/client-exports","1.docs/scripts/4.free-phone/3.exports/1.client-exports","Client Exports for the Free Phone custom-app, billing and CrewLink integrations.",{"title":972,"path":973,"stem":974,"description":975,"children":-1},"Billing Exports","/docs/scripts/free-phone/exports/billing-exports","1.docs/scripts/4.free-phone/3.exports/3.billing-exports","Billing Exports for the Free Phone custom-app, billing and CrewLink integrations.",[],1791814186174]