The endpoint plugin is already included in @kilpi/core. Apply the plugin in your Kilpi configuration as follows. This plugin is intended to be used together with @kilpi/client. 
import { createKilpi, EndpointPlugin } from "@kilpi/core";
export const Kilpi = createKilpi({  getSubject,  policies,  plugins: [    EndpointPlugin({      // Required      secret: process.env.KILPI_SECRET,    })  ]})Usage
 The endpoint plugin exposes the Kilpi.$createPostEndpoint() function which constructs a web-standard request-response handler function. You can integrate it with your framework of choice however you want. For example, with Next.js: 
// (req: Request) => Promise<Response>export const POST = Kilpi.$createPostEndpoint();Optional configuration
 You can provide additional configuration to EndpointPlugin to customize its behavior. See below examples. 
EndpointPlugin({  secret: "...",
  // Extract the `ctx` parameter from the request for `getSubject`  // if required.  getContext(req) {    return req;  },
  // Before handling the request, run this hook. This hook may  // optionally throw or return an early response to terminate  // execution. Useful for e.g. rate-limiting.  async onBeforeHandleRequest(req) {    const allow = await rateLimit();    if (!allow) {      return Response.json({ error: "Too fast" }, { status: 429 });    }  },
  // Before handling each item in the request, run this hook.  // Simply an "event-listener" type callback.  async onBeforeProcessItem(item) {    if (item.type === "fetchDecision") {      console.log(`Deciding ${item.action} on ${item.object}`);    }  },}); The API is designed for @kilpi/client and is not optimized for calling manually, however it is possible. See the short example below. 
import * as SuperJSON from "superjson";import z from "zod";
// Utility method you can call to fetch decisions from Kilpi. Supports// batching by sending multiple queries at once.export function callKilpiApiEndpoint(body: Array<{  type: "fetchDecision"; // May support other types of requests in the future, e.g. fetchSubject  requestId: string; // Generate a unique ID for this item in the array to find the corresponding response  action: string; // The action, e.g. "posts.edit"  object: unknown; // The corresponding object, e.g. the post object}>) {  // Send the request with these parameters  const response = await fetch(process.env.PUBLIC_KILPI_URL, {    method: "POST",    body: SuperJSON.stringify(body),    headers: {      "Content-Type": "application/json",      Authorization: `Bearer ${process.env.PUBLIC_KILPI_SECRET}`,    },  });
  // Response must have status 200  if (!response.ok) throw new Error("Failed to call Kilpi API");
  // Setup schema for response shape using Zod  const responseSchema =  z.object({    // Used to match which request from the body array each    // response item corresponds to.    requestId: z.string(),
    // The actual response data for that request.    data:  z.object({      decision: z.discriminatedUnion("granted", [        z.object({          granted: z.literal(true),          subject: z.unknown(),        }),        z.object({          granted: z.literal(false),          message: z.string().optional(),          reason: z.string().optional(),          metadata: z.unknown().optional(),        }),      ]),    })  }).array();
  // Get JSON and parse and validate  return await response.json().then((body) => responseSchema.parse(body));}
const responses = await callKilpiApiEndpoint([  {    type: "fetchDecision",    requestId: "123",    action: "posts.edit",    object: { id: "post-123", authorId: "user-123" },  }])
const decision = responses.find(_ => _.requestId === "123")?.data?.decision;
if (decision.granted) { ... }