---
title: 고급: API로 스케줄 실행하기
description: 고급 경로 — 인증하고, POST /v1/actions/{action_id}/runs 또는 {handle}/{slug} 버니티 경로를 호출하고, 실행 경로가 반환하는 모든 오류를 처리하는 방법을 살펴보세요.
---

대부분의 스케줄은 그냥 주기적으로 실행하면 됩니다.
[스케줄 만들기](/agents/actions/create)에서 살펴보세요. 이 페이지는 고급
경로입니다. 시계가 아니라 외부 시스템이 실행 시점을 정하게 해 줍니다.

API 호출 트리거로 직접 운영하는 서비스에서 실행을 시작하세요. 이 페이지는 실행
계약만 다룹니다. 폴링과 결과 형태는
[실행과 결과](/agents/actions/runs)에서 살펴보세요. 통신 규약상 네임스페이스는
`/v1/actions`입니다. 이 이름이 제품과 어떻게 대응하는지는
[Scheduled 개요](/agents/actions)에 있습니다.

정확한 요청·응답 스키마는 라이브 OpenAPI(`https://api.sume.com/reference/json`)에서
옵니다. 여기 표는 읽기 쉬운 요약이지 두 번째 스키마가 아닙니다.

## 사전 조건

모든 실행 요청에는 다음 세 가지가 모두 필요합니다.

1. 스케줄의 `status`가 `active`입니다.
2. 스케줄의 `api_trigger_enabled`가 `true`입니다.
3. API 키에 `actions:read`와 `actions:write`가 있습니다.

## 스코프

| 스코프 | 필요한 곳 |
|---|---|
| `actions:read` | Action 목록·조회, 실행 조회·목록입니다. |
| `actions:write` | 실행 생성, 실행 취소입니다. |

**API 호출 트리거가 나오기 전에 만든 키에는 이 스코프가 없습니다.** 예전 키는
모든 실행 요청을 `403 insufficient_scope`로 실패시키며, 기존 키에 스코프를 추가할
수는 없습니다. [API Keys](https://www.sume.com/dashboard/api-keys)에서 새 키를
만들어 교체하세요. [인증](/authentication)에서 살펴보세요.

서비스 계정 키로는 Action 실행을 만들 수 없습니다.
`403 insufficient_scope`와 `details.reason`이
`service_account_action_runs_unsupported`로 실패합니다.

## 실행하기

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

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 '{}'
```

접수된 실행은 실행 영수증과 함께 `202`를 반환합니다.

```json
{
  "data": {
    "id": "run_...",
    "object": "action.run",
    "action": { "id": "aut_...", "title": "Weekly product teaser", "trigger_type": "api" },
    "status": "queued",
    "trigger": { "source": "api", "idempotency_key": "..." },
    "status_url": "https://api.sume.com/v1/action-runs/run_.../status",
    "result_url": "https://api.sume.com/v1/action-runs/run_.../result",
    "cancel_url": "https://api.sume.com/v1/action-runs/run_.../cancel",
    "cancelable": true,
    "next_action": "poll_status",
    "idempotency_hit": false
  }
}
```

## 버니티 URL

Action은 소유자의 handle과 자신의 slug로도 실행할 수 있습니다.

```bash
curl -sS -X POST "https://api.sume.com/v1/actions/chasehuh/k-ugc/runs" \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
```

Request Body, 헤더, 스코프, idempotency, 지출 상한, 실행 영수증은 불투명 형태와
같습니다. 버니티 경로는 같은 Action으로 해석되어 같은 파이프라인을 실행합니다.
영수증의 `action.id`는 언제나 불투명한 `aut_…` id입니다.

`GET /v1/actions/{handle}/{slug}`와 `GET /v1/actions/{handle}/{slug}/runs`도
같은 방식으로 동작합니다.

**버니티 URL이 아니라 불투명 id를 저장하세요.** handle이나 Action의 slug를
바꾸면 버니티 경로가 달라지지만 `aut_…`는 절대 바뀌지 않습니다. 이름을 바꾼
handle은 90일 동안 계속 해석되지만, 이는 마이그레이션 기간이지 보장이 아닙니다.

`PublicAction`은 둘 다 노출하므로 선택할 수 있습니다.

| 필드 | 의미 |
|---|---|
| `invoke_url` | 불투명 경로입니다. 항상 존재하고 항상 영구적입니다. |
| `slug` | Action의 URL 세그먼트이거나, slug가 생기기 전에 만든 Action이면 `null`입니다. |
| `handle` | 해석 가능한 경우 소유자의 현재 handle입니다. |
| `vanity_invoke_url` | `{handle}/{slug}` 경로이거나, 둘 중 하나를 모르면 `null`입니다. |

slug는 소문자 영숫자를 하이픈 하나로 구분한 형태이며 2~64자이고 계정 안에서
고유해야 합니다. `runs`는 예약어입니다.

모르는 handle, 모르는 slug, 소유하지 않은 handle은 모두 같은
`404 action_not_found`를 반환합니다.

팀 워크스페이스가 소유한 Action은 아직 어느 경로로도 공개 API에서 접근할 수
없습니다.

## Request Body

본문은 다음 속성만 받습니다. 알 수 없는 속성은 `400`으로 거부됩니다.

| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
| `input` | 객체 | `{}` | 이 실행을 위한 호출자 데이터입니다. 최대 64개 속성, UTF-8 기준 최대 2097152바이트(2 MiB)입니다. |
| `on_active_run` | `skip` 또는 `reject` | `skip` | 이미 실행이 진행 중일 때 어떻게 할지 정합니다. |
| `generation_spend_cap_usd` | 0 이상 숫자 | Action 상한 | `min(request, Action 상한)`으로 제한됩니다. 상한을 올릴 수는 없습니다. |
| `primary_output_key` | 64자 이하 문자열 | Action 기본값 | 어떤 출력 키가 `primary_output_url`이 될지 고릅니다. |
| `output_schema` | `{ name, strict, schema }` | Action 기본값 | 요청별 출력 스키마 재정의입니다. 아래를 참고하세요. |
| `response_format` | `{ type: "json_schema", json_schema }` | — | `output_schema`의 OpenAI 형태 별칭입니다. |
| `communication.mode` | `async` 또는 `webhook` | `async` | 설명용입니다. 실제로 전달을 켜는 것은 `webhook_url`입니다. |
| `communication.webhook_url` | 2048자 이하 HTTPS URI | — | 종료 알림을 받을 대상입니다. 현재 제공 여부는 [웹훅](#웹훅)에서 살펴보세요. |

## 출력 스키마 덮어쓰기

`output_schema`는 Action이 바인딩한 값을 이 실행에 한해 덮어씁니다. 영수증에는
`output_schema.source: "request_override"`로 보고됩니다.

```json
{
  "input": { "product_name": "Aurora Headphones" },
  "output_schema": {
    "name": "sume/action-image-v1",
    "strict": true,
    "schema": {
      "type": "object",
      "additionalProperties": false,
      "required": ["caption", "image"],
      "properties": {
        "caption": { "type": ["string", "null"] },
        "image": { "$ref": "SumeMediaFile#" }
      }
    }
  },
  "primary_output_key": "image"
}
```

`output_schema.name`은 영수증에 그대로 반환됩니다. Sume는 더 좁은 문자 집합만
받는 구조화 모델을 호출할 때만 내부적으로 이름을 바꾸며, 그 변환은 API에 절대
드러나지 않습니다.

스키마는
[스케줄 만들기](/agents/actions/create#5-bind-an-output-schema-optional)가
설명하는 엄격한 부분집합을 따라야 합니다. 그 밖의 스키마는 실행이 시작되기 전에
거부됩니다.

```json
{
  "error": {
    "code": "output_schema_invalid",
    "message": "output_schema is not a satisfiable strict schema.",
    "details": {
      "violations": [
        {
          "path": "#/properties/caption",
          "rule": "required_completeness",
          "message": "Property \"caption\" must be listed in required (use a nullable type for optional values)."
        }
      ]
    },
    "next_action": "fix_input"
  }
}
```

OpenAI Structured Outputs에 익숙하다면 `response_format`도 별칭으로 받아
`output_schema`로 정규화합니다.

```json
{
  "response_format": {
    "type": "json_schema",
    "json_schema": { "name": "sume/action-image-v1", "strict": true, "schema": { } }
  }
}
```

`output_schema`와 `response_format`을 함께 보내면 `400 invalid_request`입니다.

`output_schema`는 idempotency 페이로드의 일부입니다. 같은 키를 다른 스키마로
다시 보내면 예전 영수증을 조용히 재전송하는 것이 아니라
`409 idempotency_conflict`가 됩니다.

## input이 Agent에 전달되는 방식

`input`은 펜스로 감싼 JSON 블록으로 직렬화되어 **지시문이 아니라 데이터로**
Agent에 전달됩니다. Agent의 동작은 여전히 Action에 저장된 지시문에서 옵니다.

```json
{
  "input": {
    "product_name": "Aurora Headphones",
    "campaign": "summer-2026"
  }
}
```

호출자가 보낸 텍스트는 신뢰할 수 없습니다. 지시문이 권위를 갖게 하고, `input`이
동작을 바꿀 수 있는 Action을 설계하지 마세요.
[안전한 자동화](/agents/safe-automation)에서 살펴보세요.

## Idempotency

모든 실행 요청에 `Idempotency-Key` 헤더(1~255자)를 보내세요.

- 같은 키를 같은 페이로드로 다시 보내면 원래 영수증과 `idempotency_hit: true`가
  담긴 `200`이 돌아옵니다. 두 번째 실행은 시작되지 않습니다.
- 같은 키를 다른 페이로드로 재사용하면 `409 idempotency_conflict`입니다.
- 키가 없으면 재전송 보호가 기록되지 않으며 요청마다 새 실행이 시작됩니다.

## 응답 코드

| 상태 | 의미 |
|---|---|
| `202` | 실행이 접수되어 시작됐습니다. |
| `200` | idempotency 재전송이거나, 다른 실행이 이미 진행 중이라 건너뛰었습니다. |

`200`은 작업이 끝났다는 뜻이 아닙니다. HTTP 상태가 아니라 영수증의 `status`
필드로 분기하세요.

## 중복 실행 동작

한 Action에서 동시에 활성인 실행은 하나뿐입니다. 두 번째 요청을 어떻게 처리할지는
`on_active_run`이 정합니다.

| 값 | 결과 |
|---|---|
| `skip`(기본) | `status`가 `skipped`이고 `skip_reason`이 `previous_run_active`인 영수증과 함께 `200`입니다. 실행 행은 기록됩니다. |
| `reject` | `409 action_run_in_progress`입니다. 실행이 기록되지 않습니다. |

트리거가 누락된 것을 호출 쪽에서 오류로 드러내고 싶다면 `reject`를, 중복 트리거가
예상되고 해가 없다면 `skip`을 사용하세요.

## 오류

| 상태 | `error.code` | 원인 | 해결 |
|---|---|---|---|
| `400` | `output_schema_invalid` | `output_schema.schema`가 엄격한 부분집합을 벗어났습니다. `details.violations[]`가 위반한 규칙을 알려줍니다. | 스키마를 고치세요. |
| `400` | `invalid_request` | `input`이 객체가 아니거나 속성 64개 또는 2097152바이트를 넘었습니다. `generation_spend_cap_usd`가 0 이상의 유한한 숫자가 아닙니다. `webhook_url`이 공개 HTTPS URL이 아닙니다. Action 지시문이 비어 있습니다. | 요청이나 Action을 고치세요. |
| `401` | `unauthorized` | 키가 없거나, 형식이 잘못됐거나, 폐기됐습니다. | 헤더를 확인하고 새 키를 만드세요. |
| `403` | `insufficient_scope` | 키에 `actions:write`가 없거나(`details.required_scope`), 서비스 계정 키입니다(`details.reason`). | 대시보드에서 새 키를 만드세요. |
| `404` | `action_not_found` | 알 수 없거나 보관된 Action이거나, 다른 워크스페이스 소유입니다. | `action_id`를 확인하세요. |
| `409` | `action_api_trigger_disabled` | `api_trigger_enabled`가 `false`입니다. | API 호출 트리거를 켜세요. |
| `409` | `action_inactive` | Action `status`가 `inactive`입니다. | Action을 Active로 설정하세요. |
| `409` | `action_run_in_progress` | 실행이 진행 중인데 `on_active_run`이 `reject`였습니다. | 나중에 재시도하거나 `skip`을 쓰세요. |
| `409` | `idempotency_conflict` | 같은 키를 다른 페이로드로 재사용했습니다. | 새 키를 쓰세요. |
| `429` | 요청 한도 초과 | 공개 API 요청 한도입니다. | 백오프하세요. [오류와 요청 한도](/workflows/errors-and-credits)를 참고하세요. |
| `503` | `studio_agent_upstream_unavailable` | Agents 컨트롤 플레인이 설정되지 않았거나, 도달할 수 없거나, JSON이 아닌 응답을 반환했습니다. 설정 문제일 때는 `details.missing`이 `action_control_plane`을 보고합니다. | 재시도하고, 계속되면 지원팀에 문의하세요. |

오류는 표준 Sume 오류 봉투를 사용합니다.

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This API key does not have permission to access this endpoint.",
    "request_id": "req_...",
    "category": "validation",
    "stage": "validation",
    "retryable": false,
    "retry_after_seconds": null,
    "public_reason": "insufficient_scope",
    "next_action": "fix_input",
    "details": { "required_scope": "actions:write" }
  }
}
```

OpenAPI 문서에서 클라이언트를 생성한다면, 이 라우트가 `200`, `202`, `400`,
`401`, `404`, `409`, `429`, `500`을 선언한다는 점을 유념하세요. 위의 `403`과
`503` 응답은 인증 계층과 업스트림 계층에서 발생하며 선언된 응답 집합에 없으므로,
생성된 클라이언트가 이를 모델링하지 않을 수 있습니다. 둘 다 처리하세요.

알아 둘 만한 봉투 특이점이 두 가지 있습니다.

- `insufficient_scope`는 `auth`가 아니라 `category: "validation"`으로
  분류됩니다. `auth` 카테고리에 매핑되는 것은 `401`뿐입니다.
- `studio_agent_upstream_unavailable`은 업스트림 상황을 설명하면서도
  `retryable: false`와 `next_action: "contact_support"`를 보고합니다. 제한된
  재시도는 여전히 합리적이며, 계속되면 에스컬레이션하세요.

`request_id`는 항상 로그에 남기세요. 실행을 조사받는 가장 빠른 길입니다.

## 웹훅

`communication.webhook_url`은 종료 영수증을 담은 서명된 POST를 한 번 받는 공개
HTTPS URL입니다. `callback_url`도 별칭으로 받습니다. 이벤트 이름, 봉투, 서명,
재시도 일정 등 전체 계약은 [Run 웹훅](/agents/run-webhooks)에 있습니다.

**프로덕션에서 전달이 아직 켜져 있지 않습니다.** URL은 받아서 검증하고 저장하지만
오늘 `api.sume.com`에서는 아무것도 호출하지 않습니다. 바뀔 때까지 폴링하세요.
[실행과 결과](/agents/actions/runs)에서 살펴보세요.

[웹훅](/workflows/webhooks)은 **생성 Job**의 웹훅을 다룹니다. 자체 `job.*`
이벤트 집합을 가진 별개 표면입니다. 서명 스킴은 같으므로 검증기 하나로 둘 다
처리할 수 있습니다.

## 제공하지 않는 것

- Action을 위한 MCP 도구와 CLI 명령어가 없습니다.
- Action을 만들고, 수정하고, 삭제하는 공개 엔드포인트가 없습니다. 대시보드를
  사용하세요.
- `/v1/action-runs/{run_id}/events` 엔드포인트가 없습니다. 영수증의
  `events_url`은 항상 `null`입니다.

## 다음

- [실행과 결과](/agents/actions/runs)
- [Run 웹훅](/agents/run-webhooks)
- [안전한 자동화](/agents/safe-automation)
