> For the complete documentation index, see [llms.txt](https://docs.datajet-app.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.datajet-app.com/misc/admin-actions.md).

# Admin Actions

DataJet adds **Process product**, **Process order** and **Process customer** entries to the *More actions* menu of product, order and customer pages in Shopify admin, and **Process products / orders / customers** to the bulk-selection menu of the corresponding lists. Each entry runs a script of your choice against the selected data.

## Setup

Go to **Settings → Admin actions** and pick one or more tasks for each action.

* With **one** task configured, the action runs it immediately.
* With **several** tasks, the merchant is asked which one to run.

## Script variables

Every run receives two variables: `payload`, whose shape depends on the action (see below), and `admin_action`, which describes the selection the same way for every action:

| Property                   | Value                                                                         |
| -------------------------- | ----------------------------------------------------------------------------- |
| `admin_action.ids`         | Array of the selected ids as strings. For a single object it holds one entry. |
| `admin_action.id`          | The selected id for single-object actions, `null` for bulk actions.           |
| `admin_action.object_type` | `product`, `order` or `customer`.                                             |
| `admin_action.bulk`        | `true` for the list bulk actions, `false` for single-object actions.          |

`admin_action.ids` is the recommended way to read the selection, because it looks the same for every action. Note that it is already an array: assign it directly rather than piping it through `split`, which would concatenate the ids into one string.

```liquid
{% capture query %}
  query getProduct($id: ID!) {
    product(id: $id) { title }
  }
{% endcapture %}

{% for product_id in admin_action.ids %}
  {% assign gid = "gid://shopify/Product/" | append: product_id %}
  {% json vars %}{ "id": "{{ gid }}" }{% endjson %}
  {% graphql query: query, variables: vars as result %}
  {% log "Processing " | append: result.product.title %}
{% endfor %}
```

### `payload`

| Action                   | `payload`                                                                                                  |
| ------------------------ | ---------------------------------------------------------------------------------------------------------- |
| Process order / customer | The order or customer as returned by the Shopify REST API.                                                 |
| Process product          | `{ "id": "<product id>" }`                                                                                 |
| Bulk actions             | An array with one `{ "id": "<id>" }` object per selected item, e.g. `[{ "id": "1234" }, { "id": "5678" }]` |

Single-object scripts that read `payload` keep working unchanged. For bulk actions, loop over `payload` (or `admin_action.ids`) and look up whatever else you need with the `graphql` tag.

## One script for admin actions and webhooks

The same script can serve a `products/update` webhook and the Process product actions. `admin_action` is only set for admin-action runs, so check it first and fall back to the webhook payload:

```liquid
{% if admin_action.ids != blank %}
  {% comment %}Admin action, single or bulk — always an array{% endcomment %}
  {% assign product_ids = admin_action.ids %}
{% elsif payload.id != blank %}
  {% comment %}Webhook{% endcomment %}
  {% assign product_ids = payload.id | append: "" | split: "," %}
{% else %}
  {% assign product_ids = "" | split: "," %}
{% endif %}

{% if product_ids.size == 0 %}
  {% log "No product selected" %}
  {% exit %}
{% endif %}

{% for product_id in product_ids %}
  {% comment %}…{% endcomment %}
{% endfor %}
```

Two Liquid details matter here:

* Only `nil` and `false` are falsy. An empty string and an empty array are both truthy, so guard with `product_ids.size == 0` rather than `{% unless product_ids %}`.
* `append: ""` turns the numeric webhook id into a string before `split`, so `product_ids` holds strings in every case.

## Re-running

Runs from admin actions are deduplicated for a minute, so a refresh or back navigation does not queue the same object twice. Use **Re-process** in the action modal or on the run page to run it again on purpose.
