메뉴 경로: 대시보드 > 태스크

태스크

개요

태스크는 ChatGPT·Claude 같은 AI가 여러분의 서버(워커)에서 일을 실행하게 하는 기능이에요. "우리 ERP에 A-1042 발주 넣어줘"라고 말하면, AI가 워커에 작업을 보내고 결과를 받아 대화에 보여줍니다.

  • 3Min API는 작업을 전달하고 상태를 기록할 뿐, 작업 자체는 워커가 실행합니다
  • 워커는 여러분이 직접 운영하는 HTTP 서버입니다

언제 쓰나요

  • 사내 시스템에 일을 시킬 때 — ERP 발주 등록, 재고 조회, 사내 DB 검색
  • 시간이 걸리는 작업 — 보고서 생성, 대량 변환 (최대 24시간)
  • 내 PC에서만 돌아가는 스크립트 — 터널로 공개 URL을 붙이면 됩니다 (아래 참고)

데이터를 저장하고 모으는 것이 목적이라면 엔드포인트가 맞아요.

워커 코드를 직접 짜기 어렵다면, 쓰고 있는 AI에게 "3minapi 태스크 워커 만들어줘"라고 요청해 보세요. AI는 help 도구에서 이 문서와 같은 계약을 읽고 코드를 작성합니다.

진입 경로

  • 워커 등록: 대시보드 → 태스크워커 탭 → 워커 등록
  • 실행 확인: 대시보드 → 태스크실행 기록

동작 방식

① 웹에서 워커 등록            (한 번만)
② AI → task_create            태스크 생성, 상태 working
③ 3Min API → 워커 POST        서명된 요청 전달 (callback_url 포함)
④ 워커 → 즉시 2xx 응답        "받았다"는 뜻일 뿐, 결과가 아님
⑤ 워커 → callback_url         진행 메시지(선택) → completed 또는 failed
⑥ AI → task_get               결과 확인
어디서 할 일
웹 대시보드 워커 등록·수정·삭제, 시크릿 확인·교체, 실행 기록 조회
AI 대화 워커 목록 확인, 태스크 보내기, 상태·결과 확인, 취소
워커 서버 요청 받기, 작업 처리, 결과 회신

AI는 워커의 URL과 시크릿을 볼 수 없습니다. 둘 다 웹에서만 다룹니다.

워커 등록 (웹)

항목 설명 제한
이름 필수. AI가 부르는 이름. 대소문자 구분 없이 고유, 등록 후 변경 불가 50자
설명 필수. AI가 이 설명을 보고 워커를 고릅니다 500자
워커 URL 필수. 태스크를 POST로 받을 주소 2,048바이트
인증 헤더 / 인증 값 선택. 워커 자체 인증용. 값만 넣으면 헤더는 Authorization 128 / 8,192바이트
  • 설명은 구체적으로 쓰세요. AI는 URL이 아니라 이름과 설명만 봅니다. erp-1보다 "우리 ERP에 발주를 등록하고 발주 번호를 돌려줍니다"처럼 무엇을 받아 무엇을 돌려주는지 적어야 AI가 골라요

시크릿 두 개 — 방향이 반대입니다

등록하면 시크릿 2개가 자동 발급됩니다. 워커 상세 화면(대시보드 → 태스크워커 탭 → 워커 선택)에서 시크릿 보기·시크릿 복사로 확인하세요.

시크릿 형식 방향 용도
서명 시크릿 whsec_... 3Min API → 워커 들어온 요청이 3Min API가 보낸 것인지 검증
회신 키 tm_task_... 워커 → 3Min API 회신할 때 Authorization: Bearer로 전송
  • 두 값은 서로 다르고 바꿔 쓸 수 없습니다. 엔드포인트 API 키(tm_live_·tm_test_)도 회신 키로 쓸 수 없어요
교체 동작 워커가 할 일
서명 시크릿 회전 48시간 동안 새 태스크에는 새·이전 서명이 함께 붙음. 회전 태스크의 재시도(최대 약 5시간)는 이전 서명만 이전 시크릿을 6시간 이상 유지한 뒤, 48시간 안에 교체
회신 키 재발급 이전 키 즉시 무효 (진행 중인 태스크 포함) 바로 교체

48시간 안에 한 번 더 회전하면 처음 시크릿은 즉시 무효가 됩니다. 대시보드에는 현재 시크릿만 표시되므로, 이전 시크릿을 유지하려면 회전하기 전에 복사해 두세요.

워커 구현

워커가 지켜야 할 다섯 가지

  1. 2xx를 15초 안에 돌려주세요. 작업은 응답한 뒤에 처리합니다
  2. 작업이 실패해도 요청은 2xx로 받으세요. 실패는 회신으로 알립니다 (5xx는 재시도, 그 밖의 4xx는 즉시 실패 — 응답 코드)
  3. task_id로 중복을 걸러내세요. 같은 태스크가 두 번 올 수 있습니다 (at-least-once)
  4. 서명 검증은 선택이지만 권장합니다. 안 하면 워커 URL을 아는 누구나 가짜 태스크를 보낼 수 있어요 (서명 검증)
  5. 결과는 callback_url로 회신하세요. completed 또는 failed한 번 보냅니다. 회신이 없으면 24시간 뒤 failed (결과 회신 API)

전달 요청

3Min API가 워커 URL로 보내는 요청입니다.

POST https://worker.example.com/tasks
Content-Type: application/json
webhook-id: 0199a1b2-...
webhook-timestamp: 1757650867
webhook-signature: v1,K3XkZ1n0...
X-3minapi-Task-Id: 0199a1b2-...
X-3minapi-Delivery-Attempt: 1
Authorization: Bearer xxx
헤더 내용
webhook-id 태스크 ID (task_id와 같은 값)
webhook-timestamp 보낸 시각(Unix 초). 시도마다 새로 찍힘
webhook-signature 서명. 회전 중에는 공백으로 구분된 두 개 (서명 검증)
X-3minapi-Task-Id 태스크 ID (webhook-id와 같은 값)
X-3minapi-Delivery-Attempt 몇 번째 전달 시도인지 (1부터)
등록한 인증 헤더 워커 등록 때 인증 헤더·값을 넣은 경우에만

본문:

{
	"type": "task.created",
	"timestamp": "2026-09-12T04:21:07.123Z",
	"data": {
		"task_id": "0199a1b2-...",
		"input": { "sku": "A-1042", "qty": 30 },
		"callback_url": "https://api.3minapi.com/api/v1/tasks/0199a1b2-.../result"
	}
}
필드 내용
task_id 태스크 ID. 중복 제거에 사용
input AI가 보낸 JSON 객체 (값은 같지만 키 순서는 달라질 수 있음)
callback_url 이 태스크의 회신 주소. 상태·결과는 이 주소로 보냅니다

워커의 응답 코드

응답 본문은 읽지 않고 상태 코드만 봅니다.

응답 결과
2xx 접수 완료. 회신이 올 때까지 working
5xx(501·505 제외), 429, 15초 타임아웃, 연결 오류 재시도 — 30초·2분·10분·1시간·4시간 뒤, 총 6회(약 5시간). 429의 Retry-After는 더 길 때만 반영
그 밖의 4xx(401·404·409 등), 501, 505 재시도 없이 즉시 failed
  • 서명 검증에 실패하면 401을 돌려주세요. 시크릿을 잘못 넣었다면 첫 태스크부터 바로 failed가 되어 설정 문제를 빨리 알 수 있어요

서명 검증 (선택)

webhook-signature 헤더가 서명입니다. 3Min API가 워커의 서명 시크릿으로 계산해 붙이고, 워커는 같은 계산으로 일치하는지 확인합니다.

서명 대상 = "{webhook-id}.{webhook-timestamp}.{요청 본문 원본}"
키        = 서명 시크릿에서 whsec_ 를 떼고 base64 디코드한 바이트
서명      = "v1," + base64( HMAC-SHA256(키, 서명 대상) )
  • 공식 Standard Webhooks 라이브러리를 쓰면 몇 줄로 끝납니다 (예시 코드). 언어별 패키지와 직접 구현 방법은 웹훅 서명 검증 (같은 규격)
  • 요청 본문 원본 바이트로 검증하세요. JSON으로 파싱한 뒤 다시 문자열로 만들면 반드시 실패합니다
  • webhook-timestamp가 5분 넘게 차이 나면 라이브러리가 거부합니다 (재사용 공격 차단)
  • 서명이 두 개 오면 하나라도 일치하면 통과입니다 (회전 규칙)
  • 검증하지 않는다면 최소한 callback_urlhttps://api.3minapi.com/으로 시작하는지 확인하세요 — 회신 키를 그 주소로 보내기 때문입니다

결과 회신 API

전달 요청에 담겨 온 callback_url 상태를 보냅니다. 진행 메시지는 여러 번, 완료·실패는 한 번입니다.

POST https://api.3minapi.com/api/v1/tasks/{task_id}/result
Authorization: Bearer tm_task_...
Content-Type: application/json
  • 주소: callback_url이 위 형식에 태스크 ID까지 채워져 옵니다. 직접 조립하지 말고 그대로 쓰세요
  • 회신 키: 워커 상세 화면의 회신 키시크릿 복사. 워커 서버의 환경변수 등에 넣어 두세요

진행 중 — 태스크는 working 그대로, AI가 task_get으로 메시지를 봅니다

{ "status": "working", "status_message": "2/3 재고 확인 중" }

완료

{
	"status": "completed",
	"result": { "order_id": "PO-20260912-001" },
	"status_message": "발주 PO-20260912-001 등록 완료"
}

실패

{
	"status": "failed",
	"error": { "code": 1001, "message": "재고 부족", "data": { "available": 12 } }
}
필드 규칙
status working · completed · failed 중 하나
status_message working일 때 필수, 나머지는 선택. 256자 초과분은 잘림. failed에 없으면 error.message
result completed일 때 필수. null이 아닌 아무 JSON
error failed일 때 필수. code(정수) + message(문자열), data는 선택
  • 본문 전체가 100KB 이하여야 합니다. result·error는 잘리지 않으므로 넘으면 회신 전체가 413으로 거부됩니다
  • 접수된 회신 1회 = API 호출 1회. 진행 메시지는 의미 있는 단계에서만 보내세요

회신 API 응답 코드

코드 의미 워커의 대응
202 접수됨. 보통 몇 초 안에 반영
400 본문 형식 오류, 잘못된 태스크 ID, 문자열 안의 NUL 문자 코드 수정
401 회신 키 없음·틀림 (엔드포인트 API 키 포함) 키 확인
404 이 워커의 태스크가 아님 callback_url 확인
409 이미 끝난 태스크 (완료·실패·취소) 회신 중단
413 본문 100KB 초과. 아무것도 기록되지 않음 태스크가 아직 working이면 줄여서 재전송
415 Content-Type이 JSON이 아님 헤더 수정
503 일시 장애 잠시 후 재전송

예시 코드 (Node.js)

Node.js 18 이상, package.json"type": "module"이 필요합니다.

// npm i express standardwebhooks
import express from 'express';
import { Webhook } from 'standardwebhooks';

// 서명 시크릿(whsec_...)과 회신 키(tm_task_...)는 서로 다른 값입니다
const wh = new Webhook(process.env.TASK_SIGNING_SECRET.replace('whsec_', ''));
const CALLBACK_KEY = process.env.TASK_CALLBACK_KEY;
// 받은 task_id — 실서비스에서는 DB나 Redis에 저장하세요
const seen = new Set();

const app = express();

// 서명은 원문 바이트로 계산됩니다. JSON으로 파싱하면 검증이 실패해요.
app.post('/tasks', express.raw({ type: 'application/json' }), (req, res) => {
	let envelope;
	try {
		envelope = wh.verify(req.body, {
			'webhook-id': req.header('webhook-id'),
			'webhook-timestamp': req.header('webhook-timestamp'),
			'webhook-signature': req.header('webhook-signature')
		});
	} catch {
		return res.sendStatus(401);
	}

	const { task_id, input, callback_url } = envelope.data;

	// 규칙 3 — 이미 받은 태스크면 다시 처리하지 않음
	if (seen.has(task_id)) return res.sendStatus(202);
	seen.add(task_id);

	// 규칙 1 — 먼저 응답하고, 작업은 그 뒤에
	res.sendStatus(202);
	run(input, callback_url);
});

async function run(input, callbackUrl) {
	try {
		// 진행 메시지 — 태스크는 working 그대로, AI가 task_get으로 봅니다
		await report(callbackUrl, { status: 'working', status_message: '1/2 재고 확인 중' });
		await checkStock(input); // ← 여러분의 실제 작업

		await report(callbackUrl, { status: 'working', status_message: '2/2 발주 등록 중' });
		const orderId = await createOrder(input);

		// 완료 — 결과와 요약 메시지
		await report(callbackUrl, {
			status: 'completed',
			result: { order_id: orderId },
			status_message: `발주 ${orderId} 등록 완료`
		});
	} catch (err) {
		// 규칙 2 — 작업이나 회신이 실패하면 실패로 회신
		await report(callbackUrl, {
			status: 'failed',
			error: { code: 1001, message: String(err?.message ?? err) },
			status_message: '발주 등록 실패'
		}).catch(console.error);
	}
}

// callbackUrl — 전달 요청의 callback_url을 그대로 사용
async function report(callbackUrl, body) {
	for (let attempt = 1; attempt <= 3; attempt++) {
		const res = await fetch(callbackUrl, {
			method: 'POST',
			headers: {
				Authorization: `Bearer ${CALLBACK_KEY}`,
				'Content-Type': 'application/json'
			},
			body: JSON.stringify(body)
		}).catch(() => null);

		// 202 접수, 409 이미 끝난 태스크 — 더 보낼 필요 없음
		if (res?.status === 202 || res?.status === 409) return;
		// 400·401·404·413은 다시 보내도 같은 결과
		if (res && res.status !== 503) throw new Error(`report rejected: HTTP ${res.status}`);
		// 503·네트워크 오류만 잠시 후 재시도
		if (attempt < 3) await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
	}
	throw new Error('report failed: service unavailable');
}

app.listen(8080);

로컬 PC에서 워커 실행하기

워커에는 인터넷에서 닿는 공개 URL이 필요합니다. 공유기 뒤의 PC라면 터널로 로컬 포트에 공개 HTTPS 주소를 붙이세요.

cloudflared tunnel --url http://localhost:8080
ngrok http 8080
  • 설치와 사용법: Cloudflare Quick Tunnels · ngrok 시작하기
  • 출력된 주소에 경로를 붙여(예: https://xxxx.trycloudflare.com/tasks) 워커 URL로 등록합니다
  • 계정 없는 임시 주소는 실행할 때마다 바뀝니다. 그때마다 워커 URL을 수정하거나 각 서비스의 고정 주소 기능을 쓰세요
  • PC가 꺼져 있거나 터널이 끊기면 대부분 재시도(약 5시간) 후 failed입니다. 꺼진 주소에 404를 돌려주는 서비스라면 바로 failed가 됩니다

태스크 호출 (AI)

워커를 등록하면 AI 대화에서 바로 쓸 수 있어요. 도구 이름은 몰라도 됩니다.

  • "ERP에 A-1042 30개 발주 넣어줘"
  • "아까 보낸 발주 어떻게 됐어?"
  • "방금 보낸 태스크 취소해줘"
도구 하는 일
worker_list 등록된 워커의 이름과 설명 (URL·시크릿은 보이지 않음)
task_create 워커 이름input(JSON 객체, 100KB 이하)으로 태스크 생성
task_get 태스크 한 건의 상태, 메시지, 결과 또는 에러
task_list 최근 태스크 목록 (결과 본문 제외)
task_cancel 진행 중인 태스크 취소 (최대 100건)
  • 같은 워커·같은 input을 2분 안에(타이밍에 따라 최대 약 4분) 다시 보내면 기존 태스크를 돌려줍니다. 완료·취소된 태스크도 그대로 돌려주고, 실패한 태스크만 새로 만듭니다
  • 취소는 워커에 알리지 않습니다. 남은 전달 시도만 멈추고, 이미 받은 워커의 이후 회신은 409를 받습니다

상태 확인

상태 의미 바뀌는가
working 전달 중이거나 워커가 처리 중 바뀜
completed 워커가 완료를 회신 최종
failed 워커의 실패 회신, 전달 실패, 또는 24시간 초과 최종
cancelled 취소됨 최종

failed의 원인은 status_message에 남습니다.

원인 status_message
워커가 실패 회신 워커가 보낸 메시지 (없으면 error.message)
재시도 소진 The task could not be delivered to the worker after 6 attempts (no response within 15s).
즉시 실패 응답 The task could not be delivered to the worker after 1 attempt (HTTP 401).
24시간 초과 Worker did not respond within 24 hours.

확인하는 곳:

  • AItask_get·task_list. 워커의 result요약 없이 그대로 전달됩니다
  • 실행 기록 탭. 기간(7·30·60일, Free는 7일)과 상태로 걸러 보고, 행을 누르면 입력·결과·에러·배달을 봅니다. 전달이 최종 실패했다면 배달에 시도 횟수·마지막 응답·사유가 표시됩니다

제약사항

항목
워커 개수 Free 1개 / 유료 무제한
태스크 개수·동시 실행 무제한 (월 API 호출 한도 안에서)
작업 기한 생성 후 24시간 — 넘으면 failed (매시 점검이라 최대 약 25시간)
이력 보존 유료 최소 60일 / Free는 워커 등록 7일 후 워커와 실행 기록 모두 삭제
input 크기 100KB
회신 본문 100KB (status_message는 256자에서 절단)
전달 타임아웃 · 재시도 15초 · 총 6회, 약 5시간
서명 시크릿 회전 유예 48시간
회신 키 재발급 즉시 이전 키 무효
API 호출 집계 성공한 태스크 도구 호출 1회·접수된 회신 1회 = 각 1콜. 전달·재시도는 0콜. 한도 초과여도 회신은 허용
전달 보장 at-least-once (같은 태스크가 두 번 올 수 있음)

개인정보 안내

  • 워커의 result·error마스킹 없이 AI 대화에 그대로 전달됩니다
  • 3Min API는 자유 형식 JSON에서 개인정보를 판별하지 않으니, 결과에 필요 이상의 개인정보를 담지 마세요

자주 막히는 부분

  • 모든 태스크가 바로 failed예요: 실행 기록 상세의 배달 → 마지막 응답을 보세요. 401이면 서명 시크릿 자리에 회신 키를 넣었거나, 시크릿 회전 직후 이전 시크릿을 지웠을 수 있어요
  • 태스크가 계속 working이에요: 워커가 2xx만 주고 회신을 안 보내는 경우입니다. 워커 로그에서 회신 API 응답 코드를 확인하세요
  • 회신이 401이에요: tm_task_로 시작하는 회신 키인지, 재발급 전의 키가 아닌지 확인하세요
  • 회신이 409예요: 이미 끝났거나 취소된 태스크입니다. 더 보내지 않아도 됩니다
  • 회신이 413이에요: 본문이 100KB를 넘었습니다. result를 줄여 다시 보내세요
  • 같은 요청인데 새 태스크가 안 생겨요: 2분 안의 같은 input은 기존 태스크를 돌려줍니다. 날짜 같은 값을 input에 넣어 구분하세요