---
title: 실행과 결과
description: 스케줄 실행을 폴링하고, 구조화된 출력과 산출물을 읽고, 취소하는 방법을 살펴보세요.
---

cron이든 수동이든 API든, 발동할 때마다 폴링할 수 있는 영수증이 딸린 실행이
만들어집니다. 오늘 프로덕션에서 쓸 수 있는 완료 신호는 폴링입니다.
[Run 웹훅](/agents/run-webhooks)은 루프 없이 같은 영수증을 전달하지만 아직
`api.sume.com`에서는 켜져 있지 않습니다.

## 실행 라이프사이클

```text
queued -> processing -> completed | failed | canceled | skipped
```

| 상태 | 의미 |
|---|---|
| `queued` | 접수됐지만 시작되지 않았습니다. |
| `processing` | Agent가 작업 중입니다. |
| `completed` | 끝났습니다. `output`과 `artifacts`가 채워집니다. |
| `failed` | 오류로 끝났습니다. |
| `canceled` | 취소 요청으로 중단됐습니다. |
| `skipped` | 다른 실행이 진행 중이어서 아예 실행되지 않았습니다. |

Action 어휘는 `l`을 하나만 써서 `canceled`로 표기하며, 내부 상태를 다시 매핑한
값입니다. `done`은 `completed`로, `error`는 `failed`로, `cancelled`는
`canceled`로 노출됩니다. Job 쪽 상태 문자열이 그대로 통한다고 가정하지 마세요.

## 실행 영수증

`GET /v1/action-runs/{run_id}`가 전체 영수증을 반환합니다.

| 필드 | 설명 |
|---|---|
| `id`, `object` | `object`는 `action.run`입니다. |
| `action` | `{ "id", "title", "trigger_type" }`입니다. |
| `status` | 위 표를 참고하세요. |
| `trigger` | `{ "source": "cron" \| "manual" \| "api", "idempotency_key" }`입니다. |
| `created_at`, `started_at`, `finished_at` | `started_at`과 `finished_at`은 실제로 일어나기 전까지 `null`입니다. |
| `output_schema` | `{ "name", "strict", "source" }`이며 `source`는 `default`, `action_default`, `request_override` 중 하나입니다. |
| `output` | 구조화된 결과입니다. `status`가 `completed`가 아니면 `null`입니다. |
| `output_error` | 투영이 실패했을 때의 `{ "code", "message", "details" }`입니다. `status`가 `completed`가 아니면 `null`입니다. [출력을 만들 수 없을 때](/formats/structured-output#when-output-cannot-be-produced)에서 살펴보세요. |
| `primary_output_key` | `status`가 `completed`가 아니면 `null`입니다. |
| `primary_output_url` | `primary_output_key`에 대응하는 URL입니다. `status`가 `completed`가 아니면 `null`입니다. |
| `artifacts` | 실행에서 수집한 미디어입니다. 실행이 종료될 때까지 비어 있습니다. |
| `usage` | `{ "currency": "USD", "billable_amount_usd_micros", "generation_spend_cap_usd_micros" }`이거나, 지출을 읽지 못했으면 `null`입니다. |
| `error` | `{ "code": "action_run_failed", "message" }`입니다. `status`가 `failed`일 때만 값이 있습니다. |
| `skip_reason` | 건너뛴 실행에서는 `previous_run_active`, 그 외에는 `null`입니다. |
| `request_id` | 로그에 남기세요. |
| `status_url`, `result_url`, `cancel_url` | URL을 직접 조립하지 말고 이 값을 따르세요. |
| `events_url` | 항상 `null`입니다. 실행 라이프사이클 이벤트는 API로 노출되지 않습니다. |
| `cancelable` | `queued` 또는 `processing`인 동안 `true`입니다. |
| `next_action` | 권장하는 다음 단계입니다. 아래를 참고하세요. |
| `idempotency_hit` | 이 영수증이 idempotency 재전송 결과라면 `true`입니다. |

`usage.billable_amount_usd_micros`는 이 실행에 귀속된 **생성** 지출입니다.
실행 자체의 `generation_spend_cap_usd_micros`가 대조하는 것과 같은 누적 합계이며
예약분과 확정분을 모두 셉니다. 실행 중에는 올라가고 실행이 끝나면 확정됩니다.

이 값이 아닌 것도 두 가지 있습니다. 에이전트 자체의 LLM 턴은 제외되며 그쪽은
별도의 Agent 지갑에서 과금되므로, 실행의 총비용이 아닙니다. 그리고 영수증상의
수치일 뿐 청구서가 아닙니다. 권위 있는 과금 기록은 여전히 `GET /v1/usage`입니다.

`usage` 자체가 `null`인 것은 지출을 아예 읽지 못했다는 뜻입니다. 아직 아무것도
쓰지 않았다는 뜻의 `0`과는 다릅니다.

## 완료까지 폴링하기

`GET /v1/action-runs/{run_id}/status`는 폴링 루프를 위해 간추린 페이로드를
반환합니다.

```bash
curl -sS "https://api.sume.com/v1/action-runs/$RUN_ID/status" \
  -H "Authorization: Bearer $SUME_API_KEY"
```

```json
{
  "data": {
    "id": "run_...",
    "status": "processing",
    "started_at": "2026-07-31T09:00:01.000Z",
    "finished_at": null,
    "next_action": "poll_status",
    "cancelable": true
  }
}
```

`next_action`으로 분기하세요.

| `next_action` | 언제 | 할 일 |
|---|---|---|
| `poll_status` | `queued` 또는 `processing` | 백오프를 두고 계속 폴링하세요. |
| `retry_later` | `skipped` | 다른 실행이 진행 중이었습니다. 다시 시도하세요. |
| `none` | 종료된 모든 실행 — `completed`, `failed`, `canceled` | 더 가져올 것이 없습니다. 실패한 경우 이 영수증의 `error`와 `output_error`를 읽으세요. |

이 세 가지가 전부입니다. `events_url`은 항상 `null`이고 공개 run events 경로가
없으므로, 영수증이 events를 확인하라고 안내하는 일은 없습니다.

## 결과 가져오기

`GET /v1/action-runs/{run_id}/result`는 전체 영수증을 반환하지만 실행이 종료된
뒤에만 가능합니다. 실행이 `queued`나 `processing`인 동안에는 현재 상태를
`details.status`에 담아 `409 run_not_completed`를 반환합니다.

```bash
curl -sS "https://api.sume.com/v1/action-runs/$RUN_ID/result" \
  -H "Authorization: Bearer $SUME_API_KEY"
```

## 구조화 출력

스케줄 실행은 Format 실행과 같은 구조화 출력 계약을 사용하며, 그 내용은
[구조화 출력](/formats/structured-output)에 한 번만 정리돼 있습니다. 지원되는
스키마 부분집합, `SumeMediaFile`, 내장 `sume/action-run-output/v1` 스키마, URL
게이트, `primary_output_key` 해석, `output_error` 실패 모드가 여기서도 그대로
적용됩니다.

Scheduled에만 해당하는 것은 두 가지입니다.

- 스케줄은 작성 시점이 아니라 대시보드에서 기본 스키마를 바인딩하며, 그 바인딩은
  영수증에 `output_schema.source: "action_default"`로 나타납니다.
- `POST /v1/actions/{action_id}/runs`의 요청별 `output_schema`는 그 실행에
  한해 이를 덮어쓰며 `request_override`로 나타납니다.

## 실행 목록 보기

```bash
curl -sS "https://api.sume.com/v1/actions/$ACTION_ID/runs?limit=20" \
  -H "Authorization: Bearer $SUME_API_KEY"
```

`limit`은 1~100을 받고 기본값은 50입니다. 응답은 `{ "data": [ ... ] }`입니다.
`GET /v1/actions`에는 `has_more`와 `next_cursor`가 추가되지만 페이지네이션은
구현되지 않았습니다. `has_more`는 항상 `false`, `next_cursor`는 항상
`null`입니다.

개별 실행은 해당 Action 아래
`GET /v1/actions/{action_id}/runs/{run_id}`에서도 읽을 수 있습니다.

## 실행 취소하기

```bash
curl -sS -X POST "https://api.sume.com/v1/action-runs/$RUN_ID/cancel" \
  -H "Authorization: Bearer $SUME_API_KEY"
```

취소에는 `actions:write`가 필요하며 멱등입니다. 이미 종료된 실행을 취소하면 그
종료 영수증이 `200`으로 돌아옵니다.

## 전체 예제

```bash
export SUME_API_KEY="sume_live_..."
export ACTION_ID="aut_..."

RUN=$(curl -sS -X POST "https://api.sume.com/v1/actions/$ACTION_ID/runs" \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"input":{"product_name":"Aurora Headphones"}}')

RUN_ID=$(echo "$RUN" | jq -r '.data.id')
STATUS=$(echo "$RUN" | jq -r '.data.status')

while [ "$STATUS" = "queued" ] || [ "$STATUS" = "processing" ]; do
  sleep 5
  STATUS=$(curl -sS "https://api.sume.com/v1/action-runs/$RUN_ID/status" \
    -H "Authorization: Bearer $SUME_API_KEY" | jq -r '.data.status')
done

if [ "$STATUS" = "completed" ]; then
  curl -sS "https://api.sume.com/v1/action-runs/$RUN_ID/result" \
    -H "Authorization: Bearer $SUME_API_KEY" \
    | jq -r '.data.primary_output_url'
else
  echo "run ended as $STATUS"
fi
```

프로덕션에서는 5초 고정 대기 대신 지수 백오프를 사용하세요.

## 다음

- [고급: API로 스케줄 실행하기](/agents/actions/api-trigger)
- [안전한 자동화](/agents/safe-automation)
