curl --request POST \
--url https://neo.api.projectdiscovery.io/api/v1/hooks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"action": {
"max_denials": 123,
"question": "<string>",
"reason": "<string>",
"set": {},
"text": "<string>"
},
"matcher": {
"args": [
{
"path": "<string>",
"value": "<unknown>"
}
],
"path": "<string>",
"source": [
"<string>"
],
"tool": "<string>"
},
"scope_id": "<string>"
}
'import requests
url = "https://neo.api.projectdiscovery.io/api/v1/hooks"
payload = {
"action": {
"max_denials": 123,
"question": "<string>",
"reason": "<string>",
"set": {},
"text": "<string>"
},
"matcher": {
"args": [
{
"path": "<string>",
"value": "<unknown>"
}
],
"path": "<string>",
"source": ["<string>"],
"tool": "<string>"
},
"scope_id": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
action: {
max_denials: 123,
question: '<string>',
reason: '<string>',
set: {},
text: '<string>'
},
matcher: {
args: [{path: '<string>', value: '<unknown>'}],
path: '<string>',
source: ['<string>'],
tool: '<string>'
},
scope_id: '<string>'
})
};
fetch('https://neo.api.projectdiscovery.io/api/v1/hooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://neo.api.projectdiscovery.io/api/v1/hooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'action' => [
'max_denials' => 123,
'question' => '<string>',
'reason' => '<string>',
'set' => [
],
'text' => '<string>'
],
'matcher' => [
'args' => [
[
'path' => '<string>',
'value' => '<unknown>'
]
],
'path' => '<string>',
'source' => [
'<string>'
],
'tool' => '<string>'
],
'scope_id' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://neo.api.projectdiscovery.io/api/v1/hooks"
payload := strings.NewReader("{\n \"action\": {\n \"max_denials\": 123,\n \"question\": \"<string>\",\n \"reason\": \"<string>\",\n \"set\": {},\n \"text\": \"<string>\"\n },\n \"matcher\": {\n \"args\": [\n {\n \"path\": \"<string>\",\n \"value\": \"<unknown>\"\n }\n ],\n \"path\": \"<string>\",\n \"source\": [\n \"<string>\"\n ],\n \"tool\": \"<string>\"\n },\n \"scope_id\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://neo.api.projectdiscovery.io/api/v1/hooks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"action\": {\n \"max_denials\": 123,\n \"question\": \"<string>\",\n \"reason\": \"<string>\",\n \"set\": {},\n \"text\": \"<string>\"\n },\n \"matcher\": {\n \"args\": [\n {\n \"path\": \"<string>\",\n \"value\": \"<unknown>\"\n }\n ],\n \"path\": \"<string>\",\n \"source\": [\n \"<string>\"\n ],\n \"tool\": \"<string>\"\n },\n \"scope_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://neo.api.projectdiscovery.io/api/v1/hooks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"action\": {\n \"max_denials\": 123,\n \"question\": \"<string>\",\n \"reason\": \"<string>\",\n \"set\": {},\n \"text\": \"<string>\"\n },\n \"matcher\": {\n \"args\": [\n {\n \"path\": \"<string>\",\n \"value\": \"<unknown>\"\n }\n ],\n \"path\": \"<string>\",\n \"source\": [\n \"<string>\"\n ],\n \"tool\": \"<string>\"\n },\n \"scope_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"action": {
"type": "allow",
"max_denials": 123,
"on_yes": "allow",
"question": "<string>",
"reason": "<string>",
"set": {},
"text": "<string>"
},
"created_at": "2023-11-07T05:31:56Z",
"enabled": true,
"event": "PreToolUse",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"locked": true,
"matcher": {
"args": [
{
"op": "equals",
"path": "<string>",
"value": "<unknown>"
}
],
"path": "<string>",
"source": [
"<string>"
],
"tool": "<string>"
},
"scope": "org",
"scope_id": "<string>",
"updated_at": "2023-11-07T05:31:56Z",
"editable": true
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}Create a standing policy rule
Creates a rule in the caller’s own scope, or in their workspace when they administer it. Repository and thread rules are read-only through this API.
curl --request POST \
--url https://neo.api.projectdiscovery.io/api/v1/hooks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"action": {
"max_denials": 123,
"question": "<string>",
"reason": "<string>",
"set": {},
"text": "<string>"
},
"matcher": {
"args": [
{
"path": "<string>",
"value": "<unknown>"
}
],
"path": "<string>",
"source": [
"<string>"
],
"tool": "<string>"
},
"scope_id": "<string>"
}
'import requests
url = "https://neo.api.projectdiscovery.io/api/v1/hooks"
payload = {
"action": {
"max_denials": 123,
"question": "<string>",
"reason": "<string>",
"set": {},
"text": "<string>"
},
"matcher": {
"args": [
{
"path": "<string>",
"value": "<unknown>"
}
],
"path": "<string>",
"source": ["<string>"],
"tool": "<string>"
},
"scope_id": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
action: {
max_denials: 123,
question: '<string>',
reason: '<string>',
set: {},
text: '<string>'
},
matcher: {
args: [{path: '<string>', value: '<unknown>'}],
path: '<string>',
source: ['<string>'],
tool: '<string>'
},
scope_id: '<string>'
})
};
fetch('https://neo.api.projectdiscovery.io/api/v1/hooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://neo.api.projectdiscovery.io/api/v1/hooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'action' => [
'max_denials' => 123,
'question' => '<string>',
'reason' => '<string>',
'set' => [
],
'text' => '<string>'
],
'matcher' => [
'args' => [
[
'path' => '<string>',
'value' => '<unknown>'
]
],
'path' => '<string>',
'source' => [
'<string>'
],
'tool' => '<string>'
],
'scope_id' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://neo.api.projectdiscovery.io/api/v1/hooks"
payload := strings.NewReader("{\n \"action\": {\n \"max_denials\": 123,\n \"question\": \"<string>\",\n \"reason\": \"<string>\",\n \"set\": {},\n \"text\": \"<string>\"\n },\n \"matcher\": {\n \"args\": [\n {\n \"path\": \"<string>\",\n \"value\": \"<unknown>\"\n }\n ],\n \"path\": \"<string>\",\n \"source\": [\n \"<string>\"\n ],\n \"tool\": \"<string>\"\n },\n \"scope_id\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://neo.api.projectdiscovery.io/api/v1/hooks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"action\": {\n \"max_denials\": 123,\n \"question\": \"<string>\",\n \"reason\": \"<string>\",\n \"set\": {},\n \"text\": \"<string>\"\n },\n \"matcher\": {\n \"args\": [\n {\n \"path\": \"<string>\",\n \"value\": \"<unknown>\"\n }\n ],\n \"path\": \"<string>\",\n \"source\": [\n \"<string>\"\n ],\n \"tool\": \"<string>\"\n },\n \"scope_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://neo.api.projectdiscovery.io/api/v1/hooks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"action\": {\n \"max_denials\": 123,\n \"question\": \"<string>\",\n \"reason\": \"<string>\",\n \"set\": {},\n \"text\": \"<string>\"\n },\n \"matcher\": {\n \"args\": [\n {\n \"path\": \"<string>\",\n \"value\": \"<unknown>\"\n }\n ],\n \"path\": \"<string>\",\n \"source\": [\n \"<string>\"\n ],\n \"tool\": \"<string>\"\n },\n \"scope_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"action": {
"type": "allow",
"max_denials": 123,
"on_yes": "allow",
"question": "<string>",
"reason": "<string>",
"set": {},
"text": "<string>"
},
"created_at": "2023-11-07T05:31:56Z",
"enabled": true,
"event": "PreToolUse",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"locked": true,
"matcher": {
"args": [
{
"op": "equals",
"path": "<string>",
"value": "<unknown>"
}
],
"path": "<string>",
"source": [
"<string>"
],
"tool": "<string>"
},
"scope": "org",
"scope_id": "<string>",
"updated_at": "2023-11-07T05:31:56Z",
"editable": true
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}{
"error": "Bad request",
"code": "user_spending_cap_reached",
"error_id": "<string>",
"kind": "forbidden request",
"message": "<string>",
"turnstile_site_key": "<string>"
}Authorizations
JWT authentication token
Body
What happens when the matcher matches. Each action only runs on the events that read it, and a pairing outside that is refused.
Show child attributes
Show child attributes
The moment in a task where the rule is checked.
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, SessionStart, SubagentStop, Stop Who the rule applies to. Rules from every scope are evaluated together.
org, repo, user, thread Which calls the rule covers. An absent field matches anything, so an empty
matcher covers everything within its scope. Prefer path for anything
about a file or directory: it is checked against every string in the call,
so one rule covers a read, a write, and a shell command alike.
Show child attributes
Show child attributes
Defaults to the caller for a user rule, or their workspace for an org rule.
Response
Rule created
What happens when the matcher matches. Each action only runs on the events that read it, and a pairing outside that is refused.
Show child attributes
Show child attributes
The moment in a task where the rule is checked.
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, SessionStart, SubagentStop, Stop Locked workspace policy cannot be changed through this API.
Which calls the rule covers. An absent field matches anything, so an empty
matcher covers everything within its scope. Prefer path for anything
about a file or directory: it is checked against every string in the call,
so one rule covers a read, a write, and a shell command alike.
Show child attributes
Show child attributes
Who the rule applies to. Rules from every scope are evaluated together.
org, repo, user, thread Whether this caller may change or delete the rule. Reads are wider than writes.

