File Upload & Storage
파일 업로드, 검색, 메타데이터 관리를 위한 Storage API입니다.
파일 업로드가 필요한 경우
- Image-to-Image: 참조 이미지 기반 이미지 편집/변환
- Image-to-Video: 첫 프레임 이미지로 비디오 생성
- Voice Cloning: 음성 복제를 위한 샘플 오디오
- ControlNet: 포즈/깊이 맵 등 제어 이미지
간편 업로드 (Multipart)
POST /predictions/upload를 사용하면 파일 업로드와 예측 요청을 한 번의 API 호출로 처리할 수 있습니다. 별도로 presigned URL을 발급받거나 S3에 직접 업로드할 필요가 없습니다.
파일 필드 규칙
file:<input_key> 형식으로 파일 필드명을 지정하면, 업로드된 파일의 URL이 자동으로 input[input_key]에 주입됩니다.
| 파일 필드명 | 결과 |
|---|---|
| file:image | input["image"] = "https://..." |
| file:image (x2) | input["image"] = ["url1", "url2"] |
cURL
# 단일 파일 - Image-to-Image
curl -X POST https://api.core.today/v1/predictions/upload \
-H "X-API-Key: cdt_your_api_key" \
-F "model=flux-kontext-pro" \
-F 'input={"prompt":"change background to a beach sunset"}' \
-F "file:image_url=@photo.png"
# 복수 파일 - 같은 키로 여러 파일 전송
curl -X POST https://api.core.today/v1/predictions/upload \
-H "X-API-Key: cdt_your_api_key" \
-F "model=flux-kontext-pro" \
-F 'input={"prompt":"combine these images"}' \
-F "file:image_url=@photo1.png" \
-F "file:image_url=@photo2.png"Python
import requests, json
with open("photo.png", "rb") as f:
response = requests.post(
"https://api.core.today/v1/predictions/upload",
headers={"X-API-Key": "cdt_your_api_key"},
data={
"model": "black-forest-labs/flux-kontext-pro",
"input": json.dumps({"prompt": "change background to a beach sunset"}),
},
files={"file:image_url": ("photo.png", f, "image/png")},
)
job = response.json()
print(job["job_id"]) # 이후 GET /predictions/{job_id}로 결과 조회JavaScript
const formData = new FormData();
formData.append("model", "black-forest-labs/flux-kontext-pro");
formData.append("input", JSON.stringify({ prompt: "change background to a beach sunset" }));
formData.append("file:image_url", fileInput.files[0]);
const response = await fetch("https://api.core.today/v1/predictions/upload", {
method: "POST",
headers: { "X-API-Key": "cdt_your_api_key" },
body: formData,
});
const job = await response.json();
console.log(job.job_id);Form 필드
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| model | string | Yes | 모델 ID |
| input | string (JSON) | No | 모델 입력 JSON 문자열 (기본: "{}") |
| is_public | string | No | "true" 또는 "false" (기본: "false") |
| output_folder | string | No | 결과물 저장 폴더 경로 |
| webhook_url | string | No | 완료 시 호출할 Webhook URL |
| file:<input_key> | file | No | 업로드할 파일 (최대 50MB/파일, 같은 키 복수 가능) |
참고
- 파일당 최대 크기는 50MB입니다.
- 응답 형식은
POST /predictions와 동일합니다. 이후GET /predictions/{job_id}로 결과를 조회하세요. - 업로드된 파일은 S3 스토리지에 저장되며, 7일간 접근 가능한 CDN URL이 생성됩니다.
Presigned URL 방식 (고급)
대용량 파일이나 업로드 진행률 표시가 필요한 경우, 아래 3단계 방식을 사용할 수 있습니다. 파일을 미리 업로드해두고 여러 prediction에서 재사용하려는 경우에도 유용합니다.
Presigned URL 발급
먼저 파일 업로드를 위한 presigned URL을 발급받습니다. 이 시점에 파일 메타데이터가 스토리지에 등록됩니다.
curl -X POST https://api.core.today/v1/files/upload-url \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{"filename": "reference_image.png", "folder": "inputs"}'메타데이터 저장
커스텀 메타데이터를 함께 저장할 수 있습니다 (최대 10KB):
curl -X POST https://api.core.today/v1/files/upload-url \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d {
"filename": "data",
"folder": "projects/my-project",
"metadata": {
"description": "프로젝트 데이터",
"tags": ["dataset", "training"],
"version": "1.0"
}
}응답 예시
{
"upload_url": "https://s3.ap-northeast-2.amazonaws.com/files.core.today",
"upload_fields": {
"Content-Type": "image/png",
"key": "aiapi/f04423dfab11/inputs/1765720407215_reference_image.png",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": "ASIA.../20251214/ap-northeast-2/s3/aws4_request",
"x-amz-date": "20251214T120000Z",
"x-amz-security-token": "IQoJb3JpZ2luX2VjEG0a...",
"policy": "eyJleHBpcmF0aW9uIjoiMjAyNS0xMi0xNFQxMzowMDowMFoiLCJjb25kaXRpb25zIjpbey...",
"x-amz-signature": "e6444e667072a27fd5589241803c8abf..."
},
"file_url": "https://files.core.today/aiapi/.../reference_image.png?Expires=...&Signature=...&Key-Pair-Id=...",
"object_key": "aiapi/f04423dfab11/inputs/1765720407215_reference_image.png",
"folder": "inputs"
}스토리지 쿼터 초과 (403)
팀이 플랜별 스토리지 쿼터(위 표)를 초과한 상태에서 POST /files/upload-url을 호출하면 403과 함께 storage_quota_exceeded 코드가 반환됩니다. 이는 파일당 50MB 제한과는 별개이며, 파일 하나가 아무리 작아도 팀 총 사용량이 한도를 넘으면 새 업로드가 거부됩니다. AI 예측 결과물은 이 쿼터로 차단되지 않습니다.
{
"detail": {
"message": "Storage limit exceeded. Limit: 5.0 GB, Used: 5.0 GB, Requested: 0 B",
"code": "storage_quota_exceeded"
}
}message는 사람이 읽는 설명 문자열이며 바이트/파일 개수 한도 중 어느 쪽을 넘었는지에 따라 문구가 달라집니다(변동될 수 있음) — 분기 처리는 항상 code값(storage_quota_exceeded)으로 하세요. 해결: 오래된 파일을 삭제해 사용량을 확보하거나(GET /files/storage로 현재 사용량 확인), 상위 플랜으로 업그레이드하세요.
S3에 파일 업로드
발급받은 upload_url과 upload_fields를 사용하여 S3에 직접 파일을 업로드합니다.
cURL
# upload_fields의 모든 필드를 순서대로 추가하고, file은 마지막에 추가
curl -X POST "https://s3.ap-northeast-2.amazonaws.com/files.core.today" \
-F "Content-Type=image/png" \
-F "key=aiapi/f04423dfab11/inputs/1765720407215_reference_image.png" \
-F "x-amz-algorithm=AWS4-HMAC-SHA256" \
-F "x-amz-credential=ASIA.../20251214/ap-northeast-2/s3/aws4_request" \
-F "x-amz-date=20251214T120000Z" \
-F "x-amz-security-token=IQoJb3JpZ2luX2VjEG0a..." \
-F "policy=eyJleHBpcmF0aW9uIjoiMjAyNS0xMi0xNFQxMzowMDowMFoiLCJjb25kaXRpb25zIjpbey..." \
-F "x-amz-signature=e6444e667072a27fd5589241803c8abf..." \
-F "file=@/path/to/reference_image.png"JavaScript
async function uploadFile(file, apiKey) {
// 1. Get presigned URL
const urlResponse = await fetch(
"https://api.core.today/v1/files/upload-url",
{
method: "POST",
headers: {
"X-API-Key": apiKey,
"Content-Type": "application/json"
},
body: JSON.stringify({ filename: file.name, folder: "inputs" })
}
);
const { upload_url, upload_fields, file_url } = await urlResponse.json();
// 2. Upload to S3
const formData = new FormData();
Object.entries(upload_fields).forEach(([key, value]) => {
formData.append(key, value);
});
formData.append("file", file); // file must be last!
await fetch(upload_url, {
method: "POST",
body: formData
});
// 3. Return the CDN URL (use this in prediction requests)
return file_url;
}Python
import requests
def upload_file(filepath, api_key, folder="inputs"):
filename = filepath.split("/")[-1]
# 1. Get presigned URL
url_response = requests.post(
"https://api.core.today/v1/files/upload-url",
headers={
"X-API-Key": api_key,
"Content-Type": "application/json"
},
json={"filename": filename, "folder": folder}
)
data = url_response.json()
# 2. Upload to S3
with open(filepath, "rb") as f:
files = {"file": (filename, f)}
requests.post(
data["upload_url"],
data=data["upload_fields"],
files=files
)
# 3. Return the CDN URL (use this in prediction requests)
return data["file_url"]중요
file필드는 반드시 마지막에 추가해야 합니다.- 모든
upload_fields값을 그대로 전달해야 합니다.
모델 입력에 사용
업로드 완료 후 file_url을 모델의 입력 파라미터에 사용합니다.
Image-to-Image (FLUX Kontext)
curl -X POST https://api.core.today/v1/predictions \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "black-forest-labs/flux-kontext-pro",
"input": {
"prompt": "change the background to a beach sunset",
"image_url": "https://cdn.core.today/inputs/abc123/reference_image.png"
}
}'Image-to-Video (Kling)
curl -X POST https://api.core.today/v1/predictions \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "kwaivgi/kling-v2.1",
"input": {
"prompt": "camera slowly zooms in, gentle wind blowing",
"image": "https://cdn.core.today/inputs/abc123/first_frame.jpg",
"duration": 5
}
}'Voice Cloning (Fish Speech)
curl -X POST https://api.core.today/v1/predictions \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d {
"model": "minimax/speech-02-turbo",
"input": {
"text": "이 문장은 업로드한 음성과 똑같은 목소리로 읽힙니다.",
"reference_audio": "https://cdn.core.today/inputs/abc123/voice_sample.wav"
}
}Storage API 엔드포인트
파일 업로드 외에도 스토리지 관리, 검색, 메타데이터 관리를 위한 다양한 API를 제공합니다.
| 메서드 | 엔드포인트 | 설명 |
|---|---|---|
| 업로드 | ||
| POST | /files/upload-url | Presigned URL 발급 |
| 스토리지 관리 | ||
| GET | /files/storage | 사용량 조회 (용량, 파일 수, 타입별 분류) |
| GET | /files/storage/files | 파일 목록 조회 (페이지네이션, 폴더 필터) |
| GET | /files/storage/file/{object_key} | 단일 파일 조회 (메타데이터 + 다운로드 URL) |
| GET | /files/storage/folders | 폴더 목록 조회 (파일 수, 크기) |
| POST | /files/storage/folder | 폴더 생성 (빈 폴더 미리 생성) |
| 파일 관리 | ||
| PATCH | /files/storage/file/metadata | 메타데이터 수정 |
| POST | /files/storage/file/public | 공개/비공개 설정 |
| DELETE | /files/storage/file | 파일 삭제 |
| 검색 | ||
| POST | /files/storage/search | 메타데이터 기반 파일 검색 |
| GET | /files/storage/distinct/{field} | 필드별 고유값 조회 |
| GET | /files/storage/schema/fields | 검색 가능한 필드 목록 |
스토리지 사용량 조회
현재 팀의 스토리지 사용량을 조회합니다.
curl -X GET "https://api.core.today/v1/files/storage" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"total_bytes": 52428800,
"total_mb": 50.0,
"file_count": 127,
"formatted_size": "50.0 MB",
"by_content_type": [
{"content_type": "image/png", "size": 31457280, "count": 85},
{"content_type": "video/mp4", "size": 15728640, "count": 12},
{"content_type": "audio/wav", "size": 5242880, "count": 30}
]
}응답 필드
| 필드 | 타입 | 설명 |
|---|---|---|
| total_bytes | integer | 총 사용량 (bytes) |
| total_mb | float | 총 사용량 (MB) |
| file_count | integer | 총 파일 수 |
| formatted_size | string | 포맷된 사용량 (예: "50.0 MB") |
| by_content_type | array | 콘텐츠 타입별 사용량 분류 |
파일 목록 조회
업로드된 파일 목록을 페이지네이션과 함께 조회합니다.
쿼리 파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
| folder | string | 폴더 경로로 필터링 (선택) |
| limit | integer | 반환할 파일 수 (기본값: 50) |
| offset | integer | 건너뛸 파일 수 (기본값: 0) |
| url_expiration | integer | 다운로드 URL 만료 시간 (초). 300~15,552,000 (5분~180일). 기본값: 604,800 (7일) |
# 기본 조회 (7일 만료 URL)
curl -X GET "https://api.core.today/v1/files/storage/files?folder=inputs&limit=10" \
-H "X-API-Key: cdt_your_api_key"
# 1시간 만료 URL로 조회
curl -X GET "https://api.core.today/v1/files/storage/files?folder=inputs&limit=10&url_expiration=3600" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"files": [
{
"filename": "photo.png",
"file_size": 1048576,
"content_type": "image/png",
"uploaded_at": "2025-12-14T10:00:00Z",
"object_key": "aiapi/team123/inputs/1734175200000_photo.png",
"folder": "inputs",
"expires_at": "2025-12-21T10:00:00Z",
"file_url": "https://files.core.today/..."
}
],
"total_count": 42
}단일 파일 조회
object key를 사용하여 단일 파일의 메타데이터와 다운로드 URL을 조회합니다.
object_key는 URL path에 직접 포함됩니다. S3 REST API와 동일한 방식입니다.
쿼리 파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
| url_expiration | integer | 다운로드 URL 만료 시간 (초). 300~15,552,000 (5분~180일). 기본값: 604,800 (7일) |
# 기본 조회 (7일 만료 URL)
curl -X GET "https://api.core.today/v1/files/storage/file/aiapi/team123/inputs/1734175200000_photo.png" \
-H "X-API-Key: cdt_your_api_key"
# 5분 만료 URL로 조회
curl -X GET "https://api.core.today/v1/files/storage/file/aiapi/team123/inputs/1734175200000_photo.png?url_expiration=300" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"filename": "photo.png",
"file_size": 1048576,
"content_type": "image/png",
"uploaded_at": "2025-12-14T10:00:00Z",
"object_key": "aiapi/team123/inputs/1734175200000_photo.png",
"folder": "inputs",
"status": "uploaded",
"expires_at": "2025-12-21T10:00:00Z",
"file_url": "https://files.core.today/...",
"metadata": {"project": "avatar-gen", "tags": ["portrait"]},
"is_public": false,
"public_url": null
}폴더 목록 조회
스토리지의 폴더 구조를 조회합니다. 각 폴더의 파일 수와 총 크기를 포함합니다.
curl -X GET "https://api.core.today/v1/files/storage/folders" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"folders": [
{"name": "inputs", "path": "inputs", "file_count": 85, "total_size": 31457280},
{"name": "outputs", "path": "outputs", "file_count": 30, "total_size": 15728640},
{"name": "projects", "path": "projects", "file_count": 12, "total_size": 5242880}
]
}폴더 생성
파일 업로드 전에 빈 폴더를 미리 생성할 수 있습니다. 이미 존재하는 폴더를 생성하면 기존 폴더를 반환합니다.
docs.fileUpload.createFolder.code| 파라미터 | 타입 | 설명 |
|---|---|---|
| folder_name | string (필수) | 폴더 이름 (1~255자, 특수문자 제한) |
| parent_folder | string (선택) | 상위 폴더 경로 (비어있으면 루트) |
파일 검색 API
업로드된 파일을 다양한 조건으로 검색할 수 있습니다.
요청 파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
| filters | object | 검색 조건 (메타데이터 필드 기반) |
| sort_by | string | 정렬 기준 필드 (기본값: created_at) |
| sort_order | string | "asc" 또는 "desc" (기본값: desc) |
| page | integer | 페이지 번호 (기본값: 1) |
| page_size | integer | 페이지당 결과 수 (기본값: 20, 최대: 100) |
| url_expiration | integer | 다운로드 URL 만료 시간 (초). 300~15,552,000 (5분~180일). 기본값: 604,800 (7일) |
검색 예제
# 최근 업로드된 PNG 파일 검색
curl -X POST https://api.core.today/v1/files/storage/search \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d {
"filters": {
"content_type": "image/png",
"folder": "inputs"
},
"sort_by": "created_at",
"sort_order": "desc",
"page_size": 10
}응답 예시
{
"files": [
{
"object_key": "aiapi/team123/inputs/1734175200000_photo.png",
"filename": "photo.png",
"content_type": "image/png",
"size": 1048576,
"folder": "inputs",
"created_at": "2025-12-14T10:00:00Z",
"url": "https://files.core.today/...",
"metadata": {
"project": "avatar-gen",
"tags": ["portrait", "input"]
}
}
],
"total": 42,
"page": 1,
"page_size": 10,
"total_pages": 5
}검색 가능한 메타데이터 필드
40개의 인덱싱된 필드를 사용하여 파일을 검색할 수 있습니다.
Identity 필드
file_id - 파일 고유 IDobject_key - S3 객체 키filename - 원본 파일명team_id - 팀 IDuser_id - 사용자 IDTime 필드
created_at - 생성 시간updated_at - 수정 시간expires_at - 만료 시간Source 필드
source - 출처 (upload/prediction)source_job_id - 원본 작업 IDsource_model - 생성 모델명upload_ip - 업로드 IPObject 필드
content_type - MIME 타입size - 파일 크기 (bytes)extension - 확장자folder - 폴더 경로checksum - MD5 해시Classification 필드
category - 카테고리subcategory - 서브카테고리tags - 태그 배열labels - 라벨 배열project - 프로젝트명environment - 환경 (dev/prod)Status 필드
status - 상태 (pending/active/archived)visibility - 공개 범위is_processed - 처리 완료 여부is_archived - 아카이브 여부Metrics 필드
width - 이미지/비디오 너비height - 이미지/비디오 높이duration - 오디오/비디오 길이(초)frame_count - 비디오 프레임 수sample_rate - 오디오 샘플레이트bit_depth - 오디오 비트심도AI/ML Context 필드
prompt - 생성 프롬프트negative_prompt - 네거티브 프롬프트seed - 랜덤 시드steps - 생성 스텝 수cfg_scale - CFG 스케일model_version - 모델 버전Compliance 필드
retention_days - 보관 기간legal_hold - 법적 보존 여부classification_level - 보안 등급고유값 조회 API
특정 필드의 고유값 목록을 조회합니다. 필터링 UI 구성에 유용합니다.
# 사용된 모든 폴더 목록 조회
curl -X GET "https://api.core.today/v1/files/storage/distinct/folder" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"field": "folder",
"values": ["inputs", "outputs", "projects/avatars", "projects/backgrounds"],
"count": 4
}지원 필드
folder, content_type, extension, category, project, tags, source_model, status 등
스키마 필드 조회 API
검색 가능한 모든 필드와 타입 정보를 조회합니다.
curl -X GET "https://api.core.today/v1/files/storage/schema/fields" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"fields": [
{"name": "file_id", "type": "keyword", "searchable": true},
{"name": "filename", "type": "text", "searchable": true},
{"name": "created_at", "type": "date", "searchable": true},
{"name": "size", "type": "long", "searchable": true},
{"name": "tags", "type": "keyword", "searchable": true, "array": true}
],
"total_fields": 40
}메타데이터 수정 API
파일의 메타데이터를 업데이트합니다. 기존 메타데이터와 병합됩니다.
docs.fileUpload.metaMod.code주의사항
- 시스템 필드(file_id, team_id, created_at 등)는 수정 불가
- 메타데이터 전체 크기는 10KB를 초과할 수 없음
파일 삭제 API
파일과 메타데이터를 영구 삭제합니다.
curl -X DELETE "https://api.core.today/v1/files/storage/file?object_key=aiapi/team123/inputs/1734175200000_photo.png" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"success": true,
"deleted_object_key": "aiapi/team123/inputs/1734175200000_photo.png",
"deleted_at": "2025-12-14T12:00:00Z"
}경고
- 삭제된 파일은 복구할 수 없습니다
- 자신의 팀에 속한 파일만 삭제할 수 있습니다
SDK 예제
Python - 파일 검색
import requests
API_KEY = "cdt_your_api_key"
BASE_URL = "https://api.core.today/v1"
def search_files(filters, page=1, page_size=20):
"""파일을 검색합니다."""
response = requests.post(
f"{BASE_URL}/files/storage/search",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json"
},
json={
"filters": filters,
"page": page,
"page_size": page_size,
"sort_by": "created_at",
"sort_order": "desc"
}
)
return response.json()
# 예: PNG 이미지 검색
results = search_files({
"content_type": "image/png",
"folder": "inputs"
})
for file in results["files"]:
print(f"{file['filename']} - {file['size']} bytes")
# 예: 특정 프로젝트의 파일 검색
project_files = search_files({
"project": "avatar-gen",
"tags": ["approved"]
})JavaScript - 파일 검색
const API_KEY = "cdt_your_api_key";
const BASE_URL = "https://api.core.today/v1";
async function searchFiles(filters, options = {}) {
const { page = 1, pageSize = 20, sortBy = "created_at", sortOrder = "desc" } = options;
const response = await fetch(`${BASE_URL}/files/storage/search`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
filters,
page,
page_size: pageSize,
sort_by: sortBy,
sort_order: sortOrder
})
});
return response.json();
}
// 예: 최근 업로드된 이미지 검색
const images = await searchFiles({
content_type: "image/png",
folder: "inputs"
});
console.log(`Found ${images.total} files`);
images.files.forEach(file => {
console.log(`${file.filename} - ${file.size} bytes`);
});
// 예: 특정 모델로 생성된 파일 검색
const generatedFiles = await searchFiles({
source_model: "black-forest-labs/flux-schnell",
created_at: { gte: "2025-12-01T00:00:00Z" }
});메타데이터 설계 가이드
프로젝트별 관리
{
"project": "avatar-generator",
"category": "portraits",
"environment": "production"
}워크플로우 추적
{
"status": "pending_review",
"tags": ["needs-approval", "high-priority"],
"assigned_to": "reviewer@company.com"
}AI 생성 컨텍스트
{
"source_model": "black-forest-labs/flux-schnell",
"prompt": "A professional headshot",
"seed": 12345,
"steps": 28,
"cfg_scale": 7.5
}베스트 프랙티스
- 일관된 네이밍: 모든 프로젝트에서 동일한 필드명 사용
- 태그 활용: 여러 분류가 필요한 경우 tags 배열 활용
- 환경 구분: dev/staging/production 환경별 파일 구분
- 버전 관리: version 필드로 파일 버전 추적
파일 업로드 제한
파일 형식
모든 파일 형식 지원
이미지, 비디오, 오디오, 문서, 바이너리 등
최대 크기
50MB
플랜별 스토리지 제한
파일 업로드 및 스토리지 사용은 크레딧이 차감되지 않습니다. 단, 플랜에 따라 총 용량과 파일 수에 제한이 있습니다.
| 플랜 | 파일 스토리지 | 최대 파일 수 | DB 스토리지 |
|---|---|---|---|
| Free | 1 GB | 1,000 | 100 MB |
| Pro | 10 GB | 50,000 | 5 GB |
| Team | 100 GB | 500,000 | 50 GB |
| Enterprise | 1 TB | 10,000,000 | 500 GB |
현재 사용량은 대시보드의 Storage 페이지 또는 GET /files/storage API에서 확인할 수 있습니다.
파일 유효 기간
| 항목 | 유효 기간 | 설명 |
|---|---|---|
| Presigned Upload URL | 7일 | URL 발급 후 7일 내 업로드 필요 |
| 업로드된 파일 | 플랜별 (아래 표 참고) | 보관 기간은 URL 유효 기간과 무관합니다 — URL이 만료돼도 파일은 삭제되지 않으며, 보관 기간 내에는 언제든 재서명할 수 있습니다 |
| 다운로드 URL (기본) | 7일 | 파일/결과 다운로드 URL 기본 만료 시간 |
| 다운로드 URL (커스텀, storage 조회) | 5분 ~ 180일 | storage 조회 엔드포인트(GET /files/storage/*)의 url_expiration 파라미터로 설정 (초 단위, 300~15,552,000) |
| 재서명 URL (/files/sign) | 최대 7일 | POST /files/sign의 expiration은 604,800초(7일)로 캡됩니다 |
두 상한이 다른 이유: /files/sign 재서명은 현재 단계에서 최대 7일로 캡되어 있고, storage 조회 엔드포인트에서 다운로드 URL을 직접 생성할 때는 최대 180일까지 허용됩니다. 버그가 아니라 엔드포인트별 정책 차이이며, 어느 쪽이든 URL이 만료돼도 파일은 그대로이므로 object_key를 저장해 두면 언제든 다시 발급할 수 있습니다.
| 플랜 | 파일 보관 기간 |
|---|---|
| Free | 30일 |
| Pro | 365일 |
| Team / Enterprise | 무기한 |
중요
- 업로드된 파일은 플랜별 보관 기간(위 표) 동안 유지되며, 그 전에는 직접 삭제하지 않는 한 사라지지 않습니다
- 다운로드 URL이 만료돼도 파일은 그대로입니다 — object_key를 저장해 두면
POST /files/sign으로 새 URL을 재발급받을 수 있습니다 - 스토리지 사용량은 대시보드 설정에서 확인할 수 있습니다
파일 재서명 (URL 갱신)
다운로드 URL은 기본 7일이 지나면 만료되지만, 파일 자체는 플랜별 보관 기간 동안 그대로 남아 있습니다. POST /files/sign으로 이 팀이 소유한 객체의 URL을 언제든 새로 발급받을 수 있습니다. URL 대신 object_key(또는 job_id)를 저장해 두는 것을 권장합니다 — URL은 만료되지만 object_key는 파일이 존재하는 한 유효합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
| object_key | string | 재서명할 S3 객체 키. url/job_id와 셋 중 정확히 하나만 지정 |
| url | string | 이전에 발급된 CloudFront URL (재서명용) |
| job_id | string | prediction job ID — 해당 작업이 소유한 결과 파일 전체를 한 번에 재서명 |
| expiration | integer (선택) | 요청 만료 시간(초). 최대 7일(604,800초)로 캡됨 |
docs.fileUpload.resign.code소유권 확인 후에만 URL이 발급됩니다 — 다른 팀의 객체거나 존재하지 않는 키는 동일하게 404 file_not_found로 응답합니다 (파일 존재 여부가 유출되지 않도록). 리셀러 워크스페이스에서 X-Customer-Id 헤더와 함께 호출하면 그 고객에게 귀속된 파일만 재서명 대상이 됩니다.
이전 출력 재참조 (모델 체이닝)
이전 예측의 출력 파일을 다시 다운로드/업로드하지 않고 그대로 다음 예측의 input 값으로 넘길 수 있습니다. input 안에 이 팀이 소유한 https://files.core.today/... URL이 들어 있으면, 게이트웨이가 프로바이더로 보내기 전에 자동으로 새 URL로 재서명합니다. URL이 이미 만료됐어도 소유한 파일이면 그대로 동작합니다. 예를 들어 이미지 생성 결과를 비디오 모델의 입력 이미지로 바로 연결할 수 있습니다.
- 이 팀이 소유하지 않은 파일이나
files.core.today가 아닌 URL은 변경 없이 그대로 통과합니다(에러 아님) — 아직 유효한 presigned URL이나 외부 URL도 그대로 사용할 수 있습니다. - 하나의
input에서 자동 재서명되는 소유 참조는 최대 20개입니다.
패턴 1 — 저장해 둔 URL을 그대로 넘기기 (자동 재서명)
docs.fileUpload.chaining.pattern1Code패턴 2 — object_key를 저장해 두고 /files/sign으로 새 URL 발급 후 전달
docs.fileUpload.chaining.pattern2Code패턴 1은 게이트웨이가 자동으로 재서명하므로 별도의 /files/sign 호출이 필요 없습니다. 패턴 2는 URL을 직접 검증·로깅하거나 클라이언트에 미리 전달해야 할 때 유용합니다. 두 경우 모두 파일은 다시 업로드되지 않습니다.
전체 예제 (Python)
import requests
import time
API_KEY = "cdt_your_api_key"
BASE_URL = "https://api.core.today/v1"
def upload_and_transform(image_path, prompt, project="default"):
"""이미지를 업로드하고 변환합니다."""
# 1. Get presigned URL with metadata
filename = image_path.split("/")[-1]
url_response = requests.post(
f"{BASE_URL}/files/upload-url",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={
"filename": filename,
"folder": f"projects/{project}/inputs",
"metadata": {
"project": project,
"source": "upload",
"original_filename": filename
}
}
)
url_data = url_response.json()
# 2. Upload to S3
with open(image_path, "rb") as f:
requests.post(
url_data["upload_url"],
data=url_data["upload_fields"],
files={"file": (filename, f)}
)
# 3. Create prediction with uploaded image
pred_response = requests.post(
f"{BASE_URL}/predictions",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={
"model": "black-forest-labs/flux-kontext-pro",
"input": {
"prompt": prompt,
"image_url": url_data["file_url"]
}
}
)
job_id = pred_response.json()["job_id"]
# 4. Poll for result
while True:
result = requests.get(
f"{BASE_URL}/predictions/{job_id}",
headers={"X-API-Key": API_KEY}
).json()
if result["status"] == "completed":
return result["result"]
elif result["status"] == "failed":
raise Exception(result.get("error"))
time.sleep(2)
# Usage
result = upload_and_transform(
"/path/to/photo.jpg",
"make it look like a watercolor painting",
project="art-styles"
)
print(f"Result: {result}")