> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fetchski-attract.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Artisan commands

> Every command the SDK adds: what it does, when to use it, its options, and examples to copy.

The package adds the commands on this page to Artisan. Each one asks for what it needs (Laravel
Prompts) and takes options instead in a script. Artisan's help prints a command's options, for
example the install command's:

```bash Terminal theme={"dark"}
php artisan help attract:install
```

## Set up

### Set the SDK up

Publish the config, write the site's URL, API key and signing secret to `.env`, and check the site:

```bash Terminal theme={"dark"}
php artisan attract:install
```

**When:** once, right after `composer require`, and again whenever you want to point the app at a
new key with the key in `.env`.

It publishes `config/fetchski-attract.php`, asks for the site's slug, URL, API key and the key's
signing secret (leave it empty for a token-only key), writes `FETCHSKI_ATTRACT_SITE`, `_URL`,
`_API_KEY` and `_SIGNING_SECRET` to `.env` (and empty ones to `.env.example`, when the app has one),
then runs the [check](#check-the-setup). With no secret, a `FETCHSKI_ATTRACT_SIGNING_SECRET` left in
`.env` by an earlier key is removed.

| Option | Does |
| - | - |
| `--site=` · `--url=` · `--key=` | The site's slug, public origin and API key, instead of asking |
| `--secret=` | The key's signing secret, `fss_…` (leave it out for a token-only key) |
| `--no-check` | Skips the check at the end |
| `--database` | Keeps the key in the `attract_api_keys` table, encrypted, instead of `.env` |
| `--force` | With `--database`, runs the migrations without asking |

```bash In a script theme={"dark"}
php artisan attract:install --site=acme --url=https://acme.example --key="$ATTRACT_API_KEY" --secret="$ATTRACT_SIGNING_SECRET"
```

```bash Key in the database theme={"dark"}
php artisan attract:install --database
```

With `--database` it publishes the config and the `attract_api_keys` migration, sets
`FETCHSKI_ATTRACT_SITES_FROM=database`, asks before running the migrations, then
[stores the key](#store-or-replace-the-key). The key never goes to `.env`
([Key in the database](https://docs.fetchski-attract.com/laravel/key-in-database.md)).

### Store or replace the key

Store the site's API key and signing secret in the `attract_api_keys` table, encrypted, or replace
the ones stored there:

```bash Terminal theme={"dark"}
php artisan attract:store-key
```

**When:** with the key in the database, the first time (the database install runs it for you), or to
add another site's key. To swap a stored key for a new one, [rotate it](#rotate-the-key).

It asks for the site's URL, the API key, its signing secret and its signing mode, then calls the site
with them ([the check](#check-the-setup)). The slug comes from the key, so a key for a site already in
the table replaces its row, and the old key and secret are gone at once (`Replaced the API key for
acme…`). Before asking anything it checks the table exists and that the SDK reads keys from it, and
says what to run when not.

| Option | Does |
| - | - |
| `--url=` | The site's public origin |
| `--key=` | The API key, `fsk_…` |
| `--secret=` | Its signing secret, `fss_…` (leave it out for a token-only key) |
| `--signing=` | Its signing mode: `writes`, `all` or `none` |
| `--no-check` | Skips the check at the end |

```bash In a script theme={"dark"}
php artisan attract:store-key --url=https://acme.example --key="$ATTRACT_API_KEY" --secret="$ATTRACT_SIGNING_SECRET" --signing=writes
```

> [!WARNING]
>
> A queue worker or Octane keeps the client it built until it restarts. After replacing a key,
> restart the queue workers (and reload Octane):
>
> ```bash Terminal theme={"dark"}
> php artisan queue:restart
> ```

### Rotate the key

Swap a stored site's API key and signing secret for new ones, keeping its row and URL:

```bash Terminal theme={"dark"}
php artisan attract:rotate-key
```

**When:** you have a new key for the same site from its CMS (someone left, a secret may have leaked,
or it's simply time) and want the app to use it in place of the old one.

It shows which site it's rotating, then asks for the new API key, its signing secret and its signing
mode (the current one is offered first). The new key must be for the same site: a key for another
site is refused and nothing changes. The old key and secret are gone from the table at once, then
the new key is [checked](#check-the-setup) on the site. Before asking anything it checks there's a
stored key to rotate, and says what to run when there isn't.

| Option | Does |
| - | - |
| `site` | The site's slug (default: the table's one key) |
| `--key=` | The new API key, `fsk_…` |
| `--secret=` | Its signing secret, `fss_…` (leave it out for a token-only key) |
| `--signing=` | Its signing mode: `writes`, `all` or `none` (default: the current one) |
| `--no-check` | Skips the check at the end |

```bash One of several sites theme={"dark"}
php artisan attract:rotate-key acme
```

```bash In a script theme={"dark"}
php artisan attract:rotate-key acme --key="$ATTRACT_API_KEY" --secret="$ATTRACT_SIGNING_SECRET"
```

The old key keeps working on the site until you switch it off in its CMS. Restart long-running
workers so they pick up the new key:

```bash Terminal theme={"dark"}
php artisan queue:restart
```

### Forget the key

Delete a site's key from the `attract_api_keys` table, asking first:

```bash Terminal theme={"dark"}
php artisan attract:forget-key
```

**When:** the app should stop calling the site. Then switch the key off or delete it in the CMS
under **API Keys** too: deleting the row doesn't stop the key working on the site.

| Option | Does |
| - | - |
| `site` | The site's slug (default: the table's one key) |
| `--force` | Deletes it without asking |

```bash One of several sites theme={"dark"}
php artisan attract:forget-key acme
```

```bash In a script theme={"dark"}
php artisan attract:forget-key acme --force
```

## Work with the methods

### List the methods

List every method the SDK offers, with its key, HTTP route and purpose:

```bash Terminal theme={"dark"}
php artisan attract:list
```

**When:** you're looking for the method that does what you need, or its key for the commands below.

| Option | Does |
| - | - |
| `resource` | One accessor's methods only, e.g. `blogPosts` |
| `--json` | Prints each method's entry as JSON |

```bash One accessor theme={"dark"}
php artisan attract:list blogPosts
```

```bash As JSON theme={"dark"}
php artisan attract:list --json
```

### Call a method

Call one method on the real site and print its answer as JSON:

```bash Terminal theme={"dark"}
php artisan attract:call blogPosts.listBlogs perPage=5
```

**When:** you want to see what a method really answers before writing code against it, or to try a
call by hand.

Arguments are `name=value`: numbers and `true` / `false` are cast, lists and objects are JSON, the
rest is taken as written. A write asks first. The full guide, with every argument type and worked examples, is
[Call any method from the terminal](https://docs.fetchski-attract.com/call-command.md).

| Option | Does |
| - | - |
| `method` | The method's key, e.g. `blogPosts.getBlog` |
| `args` | Its arguments, each `name=value` |
| `--site=` | A configured site (default: the default site) |
| `--force` | Calls a write without asking |

```bash Drafts too theme={"dark"}
php artisan attract:call pages.listPages aposMode=draft
```

```bash A write, without asking theme={"dark"}
php artisan attract:call blogPosts.updateBlog id=… title="Spring update" --force
```

### Write an Action

Write an Action for one method, and its test:

```bash Terminal theme={"dark"}
php artisan make:attract-action blogPosts.getBlog
```

**When:** you're adding a call to your app. The Action is the one place the call lives, and its test
fakes the site ([Testing](https://docs.fetchski-attract.com/laravel/testing.md)).

This writes `app/Actions/Attract/GetBlog.php`, an invokable Action that makes the one call, and its
test in `tests/Feature/Actions/Attract/` (Pest when the app has Pest). With no method it lets you
search them all.

| Option | Does |
| - | - |
| `method` | The method's key (none: search them) |
| `--site=` | Hard-codes one site (default: the default site) |
| `--module` · `--module=Name` | Writes into a module: on its own, config `module` |
| `--app` | Writes into the app, even when config `module` is set |
| `--pest` · `--phpunit` | Picks the test's kind |
| `--force` | Overwrites the files |

```bash Search for the method theme={"dark"}
php artisan make:attract-action
```

```bash Into a module theme={"dark"}
php artisan make:attract-action blogPosts.getBlog --module=Blog
```

In a module the files go to `modules/Blog/Actions/Attract/` and its
`Tests/Feature/Actions/Attract/`. With `FETCHSKI_ATTRACT_MODULE` set, that is where every Action goes,
with no option. The module's folder must exist.

## Check a site

### Check the setup

Check the config, that the site answers, that the key is accepted, and the clock:

```bash Terminal theme={"dark"}
php artisan attract:check
```

**When:** after setting up or rotating a key, after a deploy, or when calls start failing. The
install and store commands run it for you.

It prints one line each, ok, warn or fail: the config, the site reachable, the key accepted, and
your server's clock against the site's (a warning past a minute; signatures allow five). Exits 1 on
any failure.

| Option | Does |
| - | - |
| `--site=` | A configured site (default: the default site) |

```bash Another site theme={"dark"}
php artisan attract:check --site=globex
```

### Sweep every read

Call every read the site offers and report each as ok, refused or broken:

```bash Terminal theme={"dark"}
php artisan attract:sweep
```

**When:** after upgrading the SDK, or after a change on the site, to confirm everything your key can
read still decodes. It exits 1 when one broke.

| Option | Does |
| - | - |
| `--site=` | A configured site (default: the default site) |

## Publish what you want to change

```bash The config theme={"dark"}
php artisan vendor:publish --tag=fetchski-attract-config
```

```bash The attract_api_keys migration theme={"dark"}
php artisan vendor:publish --tag=attract-migrations
```

```bash The stubs theme={"dark"}
php artisan vendor:publish --tag=attract-stubs
```

The stubs land in `stubs/` (the Action and its tests); the Action command uses your copies from then
on.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.