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

# Get Automation Activity

> Cursor-paginated audit log for one automation

export const PlansAvailable = ({plans = [], maxPackRequired}) => {
  let displayPlans = plans;
  if (plans && plans.length === 1) {
    const lowerCasePlan = plans[0].toLowerCase();
    if (lowerCasePlan === "business") {
      displayPlans = ["Launch", "Business", "Enterprise"];
    } else if (lowerCasePlan === "premium") {
      displayPlans = ["Premium", "Launch", "Business", "Enterprise"];
    }
  }
  return <Note>
Available on {displayPlans.length === 1 ? "the " : ""}
{displayPlans.join(", ").replace(/\b\w/g, l => l.toUpperCase())}{" "}
{displayPlans.length > 1 ? "plans" : "plan"}.

{maxPackRequired && <span onClick={() => window.open('https://www.ayrshare.com/docs/additional/maxpack', '_self')} className="flex items-center mt-2 cursor-pointer">
 <span className="px-1.5 py-0.5 rounded text-sm" style={{
    backgroundColor: '#C264B6',
    color: 'white',
    fontSize: '12px'
  }}>
   Max Pack required
 </span>
</span>}
</Note>;
};

export const HeaderAPI = ({noProfileKey, profileKeyRequired}) => <>
    <ParamField header="Authorization" type="string" required>
      <a href="/docs/apis/overview#authorization">API Key</a> of the Primary Profile.
      <br />
      <br />
      Format: <code>Authorization: Bearer API_KEY</code>
    </ParamField>
    {!noProfileKey && (profileKeyRequired ? <ParamField header="Profile-Key" type="string" required>
          <a href="/docs/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField> : <ParamField header="Profile-Key" type="string">
          <a href="/docs/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField>)}
  </>;

<PlansAvailable plans={["launch", "business", "enterprise"]} maxPackRequired={false} />

<Note>
  **Beta.** See the [Automations Overview](/docs/apis/automations/overview) for the full feature description.
</Note>

Returns every dispatch attempt for one automation, newest first. Each row records the recipient, the matched trigger, every action's result, and any error.

Rows from the last **30 days** are returned. Older rows exist in Firestore (retention is indefinite for analytics) but are excluded from this endpoint for performance.

## Activity row status

Each row has a top-level `status` plus a per-action `status` inside `actionResults[]`.

| Status | When it's recorded |
| - | - |
| `pending` | Row was just written; the worker hasn't picked it up yet |
| `in_flight` | The worker is currently dispatching |
| `sent` | At least one action was **accepted** by the platform, and none hit an auth error. For `send_dm` this means Instagram accepted the message — **not** that the recipient received it (see the note below) |
| `failed` | No action was accepted, and at least one was rejected by the platform or could not run (and none hit an auth error). The reason is on `actionResults[].errorDetails` |
| `auth_error` | The account's Instagram credentials or messaging permissions could not be used — for example an expired or revoked access token, a missing Facebook Page selection, or Messaging not activated for the profile. The DM was not retried. Per-recipient delivery failures are recorded as `failed`, not here |
| `rate_limited` | The daily DM cap (tier or per-profile) was hit. No DM was sent |
| `deduplicated` | This action already fired to this recipient inside its dedup window (default 7 days, per-action configurable) |
| `skipped` | The automation was suppressed before dispatch — it became inactive or was deleted between fan-out and dispatch, it has no actions configured, or the engagement that triggered it came from the automation's own account — most often a comment on your own post. In that last case no action runs: no DM (an account cannot message itself), and no `fire_webhook` or `send_email` either. An automation that opted in to [`automation.completed`](/docs/apis/automations/overview#automation-completed) still receives that event, with this status |

`pending` and `in_flight` are transient; everything else is terminal.

When an automation has more than one action, the top-level `status` is derived from the per-action results in this order: `auth_error` if any action hit an auth error, then `sent` if any action was accepted, then `deduplicated` if every action was deduplicated, then `skipped` if every action was deliberately suppressed (skipped or deduplicated), otherwise `failed`. Read `actionResults[]` for the per-action detail.

<Note>
  **`sent` confirms Instagram accepted the message, not that the recipient received it.** Instagram exposes no delivery signal on any API surface, and if the recipient's **Message requests** setting blocks strangers, Meta accepts the send and silently drops it. See [Automation DM Sent but Not Delivered](/docs/help-center/technical-support/automation_dm_sent_but_not_delivered).
</Note>

## Per-action result fields

Each entry in `actionResults[]` describes one action's outcome:

| Field | Type | Description |
| - | - | - |
| `actionId` | string | The action's stable ID within the automation |
| `type` | string | The action type (`send_dm`, `fire_webhook`, `send_email`) |
| `status` | string | Per-action status, using the same vocabulary as the row `status` above |
| `errorDetails` | string \| `null` | Human-readable failure reason when the action did not succeed; `null` (or absent) on success. For a rejected `send_dm` this carries the mapped error message (e.g. the private-reply window is closed, or the comment already received a reply). One exception to `null` on success: a `send_dm` with `messageSource: "fallback"` is `sent` and carries the reason its callback was not used. A `fallback` that then failed to send carries the send error followed by that reason |
| `messageSource` | string | `send_dm` only. Which text the DM was built from: `template` (the static `message`), `callback` (your [`messageUrl`](/docs/apis/automations/overview#dynamic-message-content) answered and its text was used) or `fallback` (`messageUrl` was set but unusable, so `message` was used instead, with the reason on `errorDetails`). It does not say whether the DM was delivered; `status` does. Present once the DM text has been worked out, including sends that then failed. Absent when the action stopped before that (a `skipped` DM to your own account, a `deduplicated` action, or a `failed` configuration error such as a missing comment id), and for other action types |
| `config` | object (optional) | The action's configuration as it was when this fire ran, for example the `send_dm` `message` template. Stays the same if you later edit the automation, so each row shows what was configured at the time. Absent on rows written before this field was added, and when it was removed to fit the size limit (`truncated` is then `true`) |
| `sent` | object (optional) | What the action sent, or tried to send, for this fire. Recorded once the action built its message or payload, including when the send then failed: `status` says whether it went out. Absent when the action stopped before sending, for example a `skipped` result for a comment from your own account, and for `deduplicated` results. See [what `sent` contains](#what-sent-contains) |
| `truncated` | boolean (optional) | `true` when the entry was cut to fit its size limit: a long text value was shortened, or `config` and then `sent` were left out entirely. Absent otherwise |
| `completedAt` | string | ISO 8601 timestamp when the action finished |

<h3 id="what-sent-contains">
  What `sent` contains
</h3>

| Action type | `sent` fields |
| - | - |
| `send_dm` | `message`: the text the DM was sent with. That is your `message` template with its placeholders filled in for this recipient or, when `messageSource` is `callback`, the text your `messageUrl` returned. `fallbackMessage`: present only when `messageSource` is `callback`; your template, filled in, as it would have been sent without the callback |
| `send_email` | `to`, `subject` and `message`: the email as it was queued, placeholders filled in |
| `fire_webhook` | `payload`: the event fields sent to your webhook. The delivered body also carries the standard webhook fields (`action`, `created`, `hookId`, `refId`, `url`, and `timeStamp` when the webhook is signed). `hookId`: the delivery's id, present when the webhook was queued |

Long text values are cut to 4,000 characters, and `truncated` is set when that happens. Values stored under key names that look like credentials, such as `token`, `secret`, `password` or `apiKey`, are recorded as `"[redacted]"`. A `send_dm` action's `messageUrl` is always recorded as `"[redacted]"` too, because a URL can carry a secret in its path or query string.

For comment-triggered automations (`comment_keyword`), the row also carries a top-level `commentId` — the raw Instagram comment ID the DM was anchored to. It is `null` for message-driven triggers (`dm_keyword`, `story_reply`, `dm_reaction`).

Rows also carry `postId` (the post a `comment_keyword` comment was left on) and `storyId` (the story for `story_reply` and `story_comment`). Each is `null` for the other triggers, and absent on rows written before these fields were added.

## Header Parameters

<HeaderAPI noProfileKey={false} />

## Path Parameters

<ParamField path="id" type="string" required>
  The automation ID returned from `POST /automations`.
</ParamField>

## Query Parameters

<ParamField query="limit" type="number" default={25}>
  Page size. Default `25`, max `100`. Values outside `[1, 100]` are clamped; non-integer or `≤0` returns HTTP 400.
</ParamField>

<ParamField query="next" type="string">
  Opaque cursor from a previous response's `meta.pagination.next`. Omit on the first request.

  Forward-only — passing `?previous=` returns HTTP 400.
</ParamField>

## Response Shape

The response wraps the result list in `activity[]` and the cursor info in `meta.pagination`:

```json theme={"system"}
{
  "status": "success",
  "automationId": "auto_9xKp2Lm4nQ",
  "activity": [ /* …rows, newest first… */ ],
  "meta": {
    "pagination": {
      "hasMore": true,
      "limit": 25,
      "next": "eyJ0aW1lIjoiMjAyNi0wNS0xMlQwOTowMDoxMS4wMDBaIiwiaWQiOiJhdXRvXzl4S3AyTG00blE6dHJnX2ExYjJjMzpjb21tZW50X3JlY2VpdmVkOjE4MDEyMzQ1Njc4OTAwMDAwIn0="
    }
  }
}
```

`meta.pagination.next` is a base64-encoded opaque blob — treat it as a black box and pass it back unchanged in the next request's `?next=`. `meta.pagination.hasMore` is `true` when at least one more page exists.

<RequestExample>
  ```bash cURL — first page theme={"system"}
  curl \
  -H "Authorization: Bearer API_KEY" \
  -X GET "https://api.ayrshare.com/api/automations/auto_9xKp2Lm4nQ/activity?limit=25"
  ```

  ```bash cURL — next page theme={"system"}
  curl \
  -H "Authorization: Bearer API_KEY" \
  -X GET "https://api.ayrshare.com/api/automations/auto_9xKp2Lm4nQ/activity?limit=25&next=eyJ0aW1lIjoiMjAyNi0wNS0xMlQw..."
  ```

  ```javascript JavaScript theme={"system"}
  const API_KEY = "API_KEY";
  const id = "auto_9xKp2Lm4nQ";

  // Paginate through every page
  let next;
  do {
    const url = new URL(`https://api.ayrshare.com/api/automations/${id}/activity`);
    url.searchParams.set("limit", "25");
    if (next) url.searchParams.set("next", next);

    const res = await fetch(url, {
      headers: { "Authorization": `Bearer ${API_KEY}` },
    });
    const json = await res.json();
    console.log(json.activity);
    next = json.meta?.pagination?.hasMore ? json.meta.pagination.next : undefined;
  } while (next);
  ```

  ```python Python theme={"system"}
  import requests

  headers = {"Authorization": "Bearer API_KEY"}
  automation_id = "auto_9xKp2Lm4nQ"

  params = {"limit": 25}
  while True:
      r = requests.get(
          f"https://api.ayrshare.com/api/automations/{automation_id}/activity",
          params=params,
          headers=headers,
      ).json()
      print(r["activity"])
      pagination = r.get("meta", {}).get("pagination", {})
      if not pagination.get("hasMore"):
          break
      params["next"] = pagination["next"]
  ```

  ```php PHP theme={"system"}
  $id = "auto_9xKp2Lm4nQ";
  $curl = curl_init();

  curl_setopt_array($curl, [
      CURLOPT_URL => "https://api.ayrshare.com/api/automations/" . $id . "/activity?limit=25",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_CUSTOMREQUEST => "GET",
      CURLOPT_HTTPHEADER => ["Authorization: Bearer API_KEY"],
  ]);

  $response = curl_exec($curl);
  curl_close($curl);
  echo $response;
  ```
</RequestExample>

<ResponseExample>
  ```json 200: Success theme={"system"}
  {
    "status": "success",
    "automationId": "auto_9xKp2Lm4nQ",
    "activity": [
      {
        "id": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:18012345678901234",
        "automationId": "auto_9xKp2Lm4nQ",
        "triggerId": "trg_a1b2c3",
        "triggerType": "comment_keyword",
        "recipientId": "17841401234567890",
        "recipientUsername": "jane_doe",
        "eventId": "comment.received:18012345678901234",
        "commentId": "18012345678901234",
        "platform": "instagram",
        "status": "sent",
        "actionResults": [
          {
            "actionId": "act_d4e5f6",
            "type": "send_dm",
            "status": "sent",
            "errorDetails": null,
            "messageSource": "template",
            "config": { "message": "Hi {{recipient_username}}, here's the link: https://example.com" },
            "sent": { "message": "Hi jane_doe, here's the link: https://example.com" },
            "completedAt": "2026-05-12T09:14:48.000Z"
          }
        ],
        "keyword": "LINK",
        "commentText": "Send me the LINK please!",
        "created": "2026-05-12T09:14:22.000Z",
        "completedAt": "2026-05-12T09:14:48.000Z"
      },
      {
        "id": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:18012345678905555",
        "automationId": "auto_9xKp2Lm4nQ",
        "triggerId": "trg_a1b2c3",
        "triggerType": "comment_keyword",
        "recipientId": "17841405555555555",
        "recipientUsername": "late_commenter",
        "eventId": "comment.received:18012345678905555",
        "commentId": "18012345678905555",
        "platform": "instagram",
        "status": "failed",
        "actionResults": [
          {
            "actionId": "act_d4e5f6",
            "type": "send_dm",
            "status": "failed",
            "errorDetails": "Cannot send private reply: This comment is no longer eligible for a private reply.",
            "completedAt": "2026-05-12T09:12:03.000Z"
          }
        ],
        "keyword": "LINK",
        "commentText": "LINK please!",
        "created": "2026-05-12T09:12:01.000Z",
        "completedAt": "2026-05-12T09:12:03.000Z"
      },
      {
        "id": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:18012345678900000",
        "automationId": "auto_9xKp2Lm4nQ",
        "triggerId": "trg_a1b2c3",
        "triggerType": "comment_keyword",
        "recipientId": "17841409876543210",
        "recipientUsername": "repeat_user",
        "eventId": "comment.received:18012345678900000",
        "commentId": "18012345678900000",
        "platform": "instagram",
        "status": "deduplicated",
        "actionResults": [],
        "keyword": "LINK",
        "commentText": "LINK again please",
        "created": "2026-05-12T09:00:11.000Z",
        "completedAt": "2026-05-12T09:00:34.000Z"
      }
    ],
    "meta": {
      "pagination": {
        "hasMore": true,
        "limit": 25,
        "next": "eyJ0aW1lIjoiMjAyNi0wNS0xMlQwOTowMDoxMS4wMDBaIiwiaWQiOiJhdXRvXzl4S3AyTG00blE6dHJnX2ExYjJjMzoxODAxMjM0NTY3ODkwMDAwMCJ9"
      }
    }
  }
  ```

  ```json 400: Bad limit theme={"system"}
  {
    "status": "error",
    "message": "limit must be a positive integer"
  }
  ```

  ```json 400: Backward pagination not supported theme={"system"}
  {
    "status": "error",
    "message": "'previous' pagination is not supported by this endpoint"
  }
  ```

  ```json 404: Not found theme={"system"}
  {
    "action": "automation",
    "status": "error",
    "code": 469,
    "message": "Automation not found"
  }
  ```
</ResponseExample>
