SIGNALS Documentation
API Reference

API CLI

List, describe, and call Signals v1 API endpoints in-process with php artisan signals:api — auth, flags, plugin tools, scripting, and LLM instructions.

Overview

php artisan signals:api is an Artisan surface for discovering and invoking the same /api/v1 catalogue the MCP server uses. Calls run in-process through OperationInvoker (full HTTP kernel, Sanctum abilities, metering, idempotency) — not via an outbound HTTP client.

Command class: App\Console\Commands\Api\SignalsApiCommand.

Enable

  1. Open Admin → Users & Security → MCP & CLI.
  2. Enable API CLI (api_cli.enabled).
  3. For call, the acting user (token owner or --user) must hold cli.access.
  4. Mint a PAT on API Tokens when using --token, scoped to the abilities you need.

list and describe require only that the CLI is enabled. call additionally requires --token or --user and cli.access.

Authentication

Flag Behaviour
--token= Plain-text Sanctum personal access token. Abilities on that token apply to the API call. Token owner must hold cli.access.
--user= User id or email. Mints a short-lived wildcard ephemeral token for the call. User must hold cli.access.

Provide either --token or --user for call, not both. Owners pass the permission check via Gate::before.

Command reference

php artisan signals:api
    {action : list, describe, or call}
    {endpoint? : Route name (e.g. api.v1.catalogue_items.index) or plugin tool name}
    {--resource= : Filter list by catalogue resource}
    {--method= : Filter list by HTTP method}
    {--search= : Filter list by substring (route, resource, summary, uri)}
    {--plugins : List plugin MCP/CLI tools instead of catalogue endpoints}
    {--format= : Output format (list: table|json; describe: text|json; call: json|table)}
    {--param=* : Path parameter as key=value (repeatable)}
    {--query=* : Query parameter as key=value (repeatable, incl. q[...])}
    {--data= : JSON body string, or @path/to/file.json}
    {--idempotency-key= : Idempotency-Key header value}
    {--token= : Plain-text Sanctum personal access token}
    {--user= : Acting user id or email (wildcard ephemeral token)}

list

List catalogue endpoints (or plugin tools with --plugins).

Flag Notes
--resource= Exact catalogue resource segment (case-insensitive), e.g. catalogue_items
--method= HTTP method filter, e.g. GET
--search= Substring across route name, resource, summary, URI
--plugins List plugin tools exposed on the cli surface instead of API routes
--format= table (default) or json

Examples:

php artisan signals:api list
php artisan signals:api list --resource=catalogue_items --method=GET
php artisan signals:api list --search=rental --format=json
php artisan signals:api list --plugins
php artisan signals:api list --plugins --format=json

describe

Requires {endpoint} route name. Formats: text (default) or json.

php artisan signals:api describe api.v1.catalogue_items.index
php artisan signals:api describe api.v1.rentals.store --format=json

Text output includes method/URI, summary, ability, parameters, request body schema, response statuses, and SchemaRegistry filterable/sortable/searchable fields when available.

call

Invoke a catalogue endpoint or a plugin tool.

Catalogue endpoint

php artisan signals:api call api.v1.catalogue_items.index \
  --token="$SIGNALS_PAT" \
  --query="per_page=5" \
  --query="q[name_cont]=widget"

php artisan signals:api call api.v1.catalogue_items.show \
  [email protected] \
  --param="product=42"

php artisan signals:api call api.v1.catalogue_items.store \
  --token="$SIGNALS_PAT" \
  --data='{"name":"Widget","catalogue_item_type":"rental"}' \
  --idempotency-key="$(uuidgen)"

php artisan signals:api call api.v1.catalogue_items.store \
  --token="$SIGNALS_PAT" \
  --data=@./payload.json \
  --idempotency-key=job-123
Flag Notes
--param=* Path params as key=value (repeatable)
--query=* Query params as key=value (repeatable; supports q[...])
--data= JSON object string, or @path/to/file.json
--idempotency-key= Sent as Idempotency-Key
--format= json (default) or table (flat collections only; otherwise falls back to JSON with a warning)

Exit code: 0 on HTTP 2xx, 1 on failure (unknown endpoint, auth denial, non-2xx, invalid flags).

Plugin tool

Namespaced name: plugin:{package}:{tool} (package may contain /).

php artisan signals:api list --plugins
php artisan signals:api call plugin:signals/slots-demo:signals.slotsdemo.echo \
  --user=1 \
  --data='{"message":"hello"}'

Plugin call uses --data as the tool argument object (not HTTP body semantics). The acting user must pass cli.access and the tool's declared permission. Tools must expose the cli surface.

Output formats

Action Default Allowed
list table table, json
describe text text, json
call json json, table

Invalid --format prints an error and exits non-zero.

Scripting examples

# JSON pipeline: first product id
php artisan signals:api call api.v1.catalogue_items.index \
  --token="$SIGNALS_PAT" --query="per_page=1" --format=json \
  | jq '.catalogue_items[0].id'

# Discover then describe
php artisan signals:api list --search=accounts --format=json \
  | jq -r '.[].route_name' \
  | head -1 \
  | xargs -I{} php artisan signals:api describe {} --format=json

# Fail a script on API errors
set -e
php artisan signals:api call api.v1.catalogue_items.show \
  --token="$SIGNALS_PAT" --param="product=999999" \
  || echo "call failed with exit $?"

Aggregation (reporting reads)

GET /api/v1/aggregate/{model} — group-by + measure over a SchemaModelCatalog model, without paging the whole collection client-side.

# Revenue by month (money measures are summed in minor units)
php artisan signals:api call api.v1.aggregate.show \
  --token="$SIGNALS_PAT" \
  --param="model=rentals" \
  --query="group_by=starts_at" --query="bucket=month" \
  --query="measure=charge_total" --query="fn=sum"

# Outstanding by account, filtered to issued invoices
php artisan signals:api call api.v1.aggregate.show \
  --token="$SIGNALS_PAT" \
  --param="model=invoices" \
  --query="group_by=account_id" --query="measure=amount_outstanding" --query="fn=sum" \
  --query="q[status_eq]=issued"

Bulk writes

POST {resource}/bulk accepts a {resource} array of row payloads (same shape as the single-row warehouse endpoint) and returns per-row results — HTTP 201 when every row succeeds, 207 (Multi-Status) when some rows fail so the caller can retry only the failed indices.

php artisan signals:api call api.v1.activities.bulk \
  --token="$SIGNALS_PAT" \
  --data='{"activities":[{"title":"Call A"},{"title":"Call B"}]}'

php artisan signals:api call api.v1.accounts.bulk \
  --token="$SIGNALS_PAT" \
  --data=@./accounts-batch.json \
  --idempotency-key="$(uuidgen)"

Dry-run (preview before mutating)

Rental transition endpoints (convert_to_quote, convert_to_order, change_status) accept dry_run=true (query or body) to run the guard pipeline and report {allowed, reason, code} without applying the transition — useful for an agent that wants to check feasibility before spending a mutating call.

php artisan signals:api call api.v1.rentals.change_status \
  --token="$SIGNALS_PAT" \
  --param="rental=42" \
  --data='{"status_id":1,"dry_run":true}'

Cursor pagination

List endpoints accept cursor=1 (start) in place of page for large sweeps — the response meta carries next_cursor/prev_cursor instead of total/page, avoiding the cost of an offset count on big tables.

php artisan signals:api call api.v1.activities.index \
  --token="$SIGNALS_PAT" \
  --query="cursor=1" --query="per_page=100" --query="sort=id"
Exit code Meaning
0 Success (list/describe completed, or call returned 2xx)
1 Settings disabled, unknown action/endpoint, auth/permission denial, invalid options, or non-2xx call

LLM instructions

  1. Confirm api_cli.enabled and that the acting principal has cli.access before mutating.
  2. Prefer list --search= / --resource= to discover route names, then describe before call.
  3. Use curated MCP tools when connected over MCP; use this CLI for shell/automation and CI.
  4. On writes, pass --idempotency-key when the API or api.require_idempotency_key requires it.
  5. Scope --token abilities to the job; use --user only in trusted local/ops contexts (wildcard ephemeral token).
  6. Parse --format=json with jq; treat exit code 1 as failure.
  7. Plugin tools: list --plugins, then call plugin:{package}:{tool} with --data args.
  8. Reach for api.v1.aggregate.show before paging a whole collection to sum a measure client-side (revenue by month, outstanding by account, etc.).
  9. Prefer {resource}/bulk over N sequential single-row calls when creating more than a handful of rows; check each row's status in the results array — a 207 response means partial success, not failure.
  10. Pass dry_run=true on rental transition endpoints (change_status, convert_to_quote, convert_to_order) to check {allowed, reason, code} before spending a mutating call.
  11. Use --query="cursor=1" (then follow meta.next_cursor) instead of page= when sweeping a large collection.