> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.claw-ops.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.claw-ops.com/_mcp/server.

# 이메일 단건 조회 (본문 포함)

GET https://api.claw-ops.com/v1/accounts/{accountId}/emails/{emailId}

이메일 한 건을 조회합니다. 목록 항목의 모든 필드에 **본문과 첨부 목록**이 더해집니다.

받은 메일이면 `text`·`html` 에 본문이, `attachments` 에 첨부(각각 단기 내려받기 링크
포함)가, `raw` 에 원문 전체를 받는 단기 링크가 담깁니다. 보낸 메일은 이 필드들이 모두
`null` 입니다 — 보낸 본문은 여러분이 만든 것이라 우리가 따로 보관하지 않습니다.

⚠️ 응답에 담긴 링크들은 **잠시 뒤 만료됩니다**(`expiresAt`). 저장해 두지 말고 필요할 때
다시 호출하십시오.

⚠️ 보관기간이 지나면 본문과 `raw` 가 `null` 이 됩니다. 그때도 **발신자·제목·시각·판정
같은 메타데이터는 그대로 조회됩니다** — 메일이 왔다는 사실은 사라지지 않습니다.

다른 계정의 메일은 조회할 수 없습니다. 남의 것을 요청하면 **없는 것과 같은 `404`** 가
돌아옵니다 — 구분해서 알리면 그 ID 가 실재하는지 알아낼 수 있기 때문입니다.

Reference: https://docs.claw-ops.com/api-reference/claw-ops-api/emails/get-email

## Authentication

- `Authorization` header (bearer token, required) — API Key를 Bearer 토큰으로 전달

## Request

### Path parameters

- `accountId` (string, required) — 계정 ID
- `emailId` (string, required) — 발송 응답의 `emailId`, 목록의 `emailId`, 또는 `email.received` 웹훅의 `EmailId`.

## Response

### 200

이메일 한 건

- `emailId` (string, required) — ClawOps 이메일 리소스 ID.
- `direction` (enum, required) — `outbound` 는 이 계정이 보낸 메일, `inbound` 는 이 계정의 **수신 도메인으로 들어온** 메일입니다.
  - Allowed values: `outbound`, `inbound`
- `status` (enum, required) — 보낸 메일은 `sent`(공급자 접수) 또는 `failed` 입니다 — `sent` 는 **접수됐다**는 뜻이지 수신함 도착이 아닙니다. 받은 메일은 항상 `received` 입니다.
  - Allowed values: `sent`, `failed`, `received`
- `from` (string, required)
- `to` (list of string, required)
- `recipientCount` (integer, required) — 보낸 메일에서는 **과금 단위**입니다 — 실제로 발송한 To+Cc+Bcc 의 합계이며 한 통을 3명에게 보내면 3입니다. **수신거부로 빠진 주소는 세지 않습니다**(`suppressed` 참고). 받은 메일에서는 **이 계정에 실제로 배달된 주소 수**(`recipients` 의 길이)입니다.
- `createdAt` (datetime, required)
- `bodyTruncated` (boolean, required) — 본문이 상한(각 1MiB)에 걸려 잘렸는지. 잘린 경우에도 **원문에는 전부 있으므로** `raw.downloadUrl` 로 받아 직접 해석할 수 있습니다.
- `cc` (list of string, optional)
- `bcc` (list of string, optional)
- `suppressed` (list of string, optional) — 수신거부 명단에 있어 이 발송에서 **빠진** 주소들. 요청한 표기 그대로 돌려줍니다. ⚠️ 이메일은 차단된 수신자가 있어도 **나머지에게는 보냅니다**(전화·문자가 발신 전체를 거절하는 것과 다릅니다). 그래서 `201` 을 받고도 일부가 안 갔을 수 있고, **이 칸이 그걸 알려주는 유일한 자리**입니다. 비어 있으면 아무도 빠지지 않았다는 뜻입니다. `recipientCount` 에는 포함되지 않습니다 — 요청한 수신자 수는 `recipientCount + suppressed.length` 입니다. 받은 메일에서는 항상 빈 배열입니다.
- `subject` (string, optional, nullable)
- `attachmentCount` (integer, optional)
- `failureCode` (string, optional, nullable) — 실패 사유 코드. `status` 가 `sent` 면 `null` 입니다. `provider_unavailable` 은 **공급자를 부르지 못한** 경우이고, 그 밖의 값은 공급자가 거절한 경우입니다.
- `failureMessage` (string, optional, nullable) — 사람이 읽는 실패 설명. `status` 가 `sent` 면 `null` 입니다.
- `replyTo` (list of string, optional) — 답장 주소(`Reply-To`). 받은 메일은 발신자가 지정한 주소, 보낸 메일은 발송할 때 지정한 값입니다. ⭐ **답장은 `from` 이 아니라 이 주소로 보내십시오.** 비어 있을 때만 `from` 이 답장 주소입니다(RFC 5322 §3.6.2). 뉴스레터·티켓 시스템은 대부분 이 값을 다르게 둡니다.
- `messageId` (string, optional, nullable) — 이 메일 자신의 `Message-ID`(꺾쇠 `< >` 제외). **답장할 때 `inReplyTo` 에 그대로 넣는 값입니다.** ⛔ **보낸 메일은 항상 `null`** 입니다. 이 헤더는 공급자가 발급하며 우리가 지정한 값을 덮어쓰기 때문에 우리도 그 값을 모릅니다. ⚠️ 받은 메일도 `null` 일 수 있습니다 — 이 헤더는 RFC 상 필수가 아니고, 붙이지 않고 보내는 발신자가 실제로 있습니다. 그 메일에는 스레드로 답장할 수 없습니다.
- `inReplyTo` (string, optional, nullable) — 이 메일이 답장한 원본의 `Message-ID`(꺾쇠 제외). 답장이 아니면 `null` 입니다.
- `references` (list of string, optional) — 대화의 조상 사슬. **오래된 것이 앞**입니다. 다음 답장에 넘길 값은 `[...references, messageId]` 입니다. ⚠️ 보낸 메일의 값은 요청하신 목록이 아니라 **실제로 헤더에 실어 보낸 목록**입니다. 긴 대화는 공급자 헤더 길이 상한 때문에 RFC 5537 §3.4.4 에 따라 첫 항목과 최근 항목만 남기고 줄여 보냅니다.
- `recipients` (list of string, optional, nullable) — **실제로 이 계정에 배달된 주소.** 받은 메일에만 있습니다(보낸 메일은 `null`). ⚠️ **`to` 를 보고 라우팅하지 마십시오.** 숨은 참조(BCC)로 온 메일은 `to` 에 그 주소가 **없습니다** — 이 필드에만 있습니다. 한 도메인에 `support@`·`sales@` 처럼 여러 주소를 두고 처리를 나눈다면 반드시 이 값을 쓰십시오.
- `receivedAt` (datetime, optional, nullable) — 공급자가 메일을 받은 시각. 받은 메일에만 있습니다.
- `sizeBytes` (integer, optional, nullable) — 받은 메일의 원문 크기(바이트). ⚠️ 공급자가 덧붙인 헤더(약 3.6KB)가 포함된 값이라 메일 클라이언트가 보여 주는 크기와 다를 수 있습니다. **과금은 이 값 기준입니다.**
- `verdicts` (EmailDetailResponseVerdicts, optional, nullable) — 공급자의 스팸·바이러스·인증 판정. 받은 메일에만 있습니다. ⚠️ **값은 공급자 원문 그대로**입니다(`PASS`·`FAIL`·`GRAY`·`PROCESSING_FAILED` 등). 우리가 해석하지 않으므로 목록이 늘어날 수 있습니다 — 모르는 값을 만나면 안전한 쪽으로 처리하십시오. ⛔ **`from` 만 믿고 자동 처리하지 마십시오.** 발신자는 위조될 수 있고, 그걸 판별하라고 `spf`·`dkim`·`dmarc` 를 함께 드립니다.
- `parseStatus` (enum, optional, nullable) — 받은 메일의 본문 해석 결과. `unparsable` 이면 본문을 읽지 못한 것이며, 그때도 원문은 그대로 보관되어 단건 조회의 `raw` 로 받을 수 있습니다.
  - Allowed values: `parsed`, `unparsable`
- `text` (string, optional, nullable) — 평문 본문. 받은 메일에만 있습니다. 보관기간이 지났거나 본문을 해석하지 못한 경우 `null` 입니다 — 그때도 발신자·제목· 시각 같은 메타데이터는 그대로 조회됩니다.
- `html` (string, optional, nullable) — HTML 본문. 받은 메일에만 있습니다. ⛔ **이 HTML 은 외부인이 보낸 것이며 우리가 소독(sanitize)하지 않습니다.** 원문을 손실 없이 전달하는 것이 이 API 의 계약이므로, 그대로 화면에 렌더하면 스크립트 삽입(XSS)에 노출됩니다. 표시하려면 여러분 쪽에서 정제하십시오. ### 본문 안의 이미지 HTML 메일의 이미지는 첨부로 실려 오고 본문은 `<img src="cid:로고">` 처럼 그것을 가리킵니다. 그대로 렌더하면 이미지가 깨지므로, `cid:` 뒤의 값과 같은 `contentId` 를 가진 첨부를 찾아 그 `downloadUrl` 로 바꿔 넣으십시오. 그 URL 은 인증이 필요 없어 브라우저에서 바로 표시됩니다.
- `attachments` (list of EmailDetailResponseAttachmentsItems, optional, nullable) — 첨부 목록. 받은 메일에만 있습니다.
- `raw` (EmailDetailResponseRaw, optional, nullable) — 원문(MIME) 전체를 내려받는 **단기 링크**입니다. 받은 메일에만 있고, 보관기간이 지나면 `null` 입니다. 본문이 잘렸거나(`bodyTruncated`) 우리가 해석하지 못한 메일도 원문에는 전부 있습니다. ⚠️ 이 URL 은 **인증을 거치지 않습니다** — 가진 사람은 누구나 받을 수 있으니 로그나 외부에 남기지 마십시오. 그리고 **저장해 두고 나중에 쓰지 마십시오**: `expiresAt` 이후에는 동작하지 않습니다.

## Errors

### 401 Unauthorized Error

인증 실패

- `error` (string, optional) — 사람이 읽을 수 있는 에러 메시지
- `code` (string, optional) — 기계 판독용 에러 코드. **분기는 이 값으로 하세요** — `error` 문구는 안내를 다듬으면서 바뀔 수 있지만 코드는 계약입니다. 같은 상태 코드라도 대응이 갈리는 경우가 있습니다. 예를 들어 발신의 `422` 는 - `recipient_blocked`: 수신거부 명단에 있는 번호입니다. 재시도하지 말고 명단에서 해제해야 발신됩니다. - `quota_exceeded`: 플랜 한도를 넘었습니다. 다음 주기에 다시 발신됩니다. 전화번호 정규화 관련: - `INVALID_PHONE_NUMBER`: 전화번호 포맷 오류 (한국 번호 형식 아님) - `UNSUPPORTED_COUNTRY`: 국제 발신 미지원 (+82 외 국가코드)
- `ip` (string, optional) — `overseas_ip_blocked` 일 때만 옵니다. 국외 IP 라고 판정한 요청 IP 입니다. 해외 리전 클라우드 서버에서 호출한다면 이 값이 그 서버의 공인 IP 이며, 콘솔 「국외 접속 예외」에서 예외를 신청할 때 이 고정 IP 를 적습니다.

### 403 Forbidden Error

접근 권한 없음

- `error` (string, optional) — 사람이 읽을 수 있는 에러 메시지
- `code` (string, optional) — 기계 판독용 에러 코드. **분기는 이 값으로 하세요** — `error` 문구는 안내를 다듬으면서 바뀔 수 있지만 코드는 계약입니다. 같은 상태 코드라도 대응이 갈리는 경우가 있습니다. 예를 들어 발신의 `422` 는 - `recipient_blocked`: 수신거부 명단에 있는 번호입니다. 재시도하지 말고 명단에서 해제해야 발신됩니다. - `quota_exceeded`: 플랜 한도를 넘었습니다. 다음 주기에 다시 발신됩니다. 전화번호 정규화 관련: - `INVALID_PHONE_NUMBER`: 전화번호 포맷 오류 (한국 번호 형식 아님) - `UNSUPPORTED_COUNTRY`: 국제 발신 미지원 (+82 외 국가코드)
- `ip` (string, optional) — `overseas_ip_blocked` 일 때만 옵니다. 국외 IP 라고 판정한 요청 IP 입니다. 해외 리전 클라우드 서버에서 호출한다면 이 값이 그 서버의 공인 IP 이며, 콘솔 「국외 접속 예외」에서 예외를 신청할 때 이 고정 IP 를 적습니다.

### 404 Not Found Error

`email_not_found` — 없거나 이 계정의 메일이 아님.

- `error` (string, optional) — 사람이 읽을 수 있는 에러 메시지
- `code` (string, optional) — 기계 판독용 에러 코드. **분기는 이 값으로 하세요** — `error` 문구는 안내를 다듬으면서 바뀔 수 있지만 코드는 계약입니다. 같은 상태 코드라도 대응이 갈리는 경우가 있습니다. 예를 들어 발신의 `422` 는 - `recipient_blocked`: 수신거부 명단에 있는 번호입니다. 재시도하지 말고 명단에서 해제해야 발신됩니다. - `quota_exceeded`: 플랜 한도를 넘었습니다. 다음 주기에 다시 발신됩니다. 전화번호 정규화 관련: - `INVALID_PHONE_NUMBER`: 전화번호 포맷 오류 (한국 번호 형식 아님) - `UNSUPPORTED_COUNTRY`: 국제 발신 미지원 (+82 외 국가코드)
- `ip` (string, optional) — `overseas_ip_blocked` 일 때만 옵니다. 국외 IP 라고 판정한 요청 IP 입니다. 해외 리전 클라우드 서버에서 호출한다면 이 값이 그 서버의 공인 IP 이며, 콘솔 「국외 접속 예외」에서 예외를 신청할 때 이 고정 IP 를 적습니다.

## Types

### EmailDetailResponseVerdicts

공급자의 스팸·바이러스·인증 판정. 받은 메일에만 있습니다. ⚠️ **값은 공급자 원문 그대로**입니다(`PASS`·`FAIL`·`GRAY`·`PROCESSING_FAILED` 등). 우리가 해석하지 않으므로 목록이 늘어날 수 있습니다 — 모르는 값을 만나면 안전한 쪽으로 처리하십시오. ⛔ **`from` 만 믿고 자동 처리하지 마십시오.** 발신자는 위조될 수 있고, 그걸 판별하라고 `spf`·`dkim`·`dmarc` 를 함께 드립니다.

- `spam` (string, optional, nullable)
- `virus` (string, optional, nullable)
- `spf` (string, optional, nullable)
- `dkim` (string, optional, nullable)
- `dmarc` (string, optional, nullable)

### EmailDetailResponseAttachmentsItems

- `attachmentId` (string, required)
- `sizeBytes` (integer, required)
- `filename` (string, optional, nullable) — ⚠️ **보낸 사람이 정한 값**입니다. 파일 시스템 경로로 그대로 쓰지 마십시오.
- `contentType` (string, optional, nullable)
- `contentId` (string, optional, nullable) — 본문의 `<img src="cid:...">` 가 가리키는 값입니다. 인라인 이미지를 이 첨부와 잇는 유일한 키이며, 일반 첨부는 `null` 입니다.
- `inline` (boolean, optional) — 본문에 삽입된 이미지인지(`true`), 별도 첨부인지(`false`).
- `downloadUrl` (string, optional, nullable) — 이 첨부를 받는 **단기 링크**입니다. 인증이 필요 없어 `<img src>` 에 그대로 넣어도 표시됩니다. ⚠️ **저장해 두고 나중에 쓰지 마십시오** — `expiresAt` 이후에는 동작하지 않습니다. 필요할 때 이 API 를 다시 호출하거나 첨부 내려받기 API 를 쓰십시오. 보관기간이 지난 첨부는 `null` 입니다.
- `expiresAt` (datetime, optional, nullable) — `downloadUrl` 이 만료되는 시각.

### EmailDetailResponseRaw

원문(MIME) 전체를 내려받는 **단기 링크**입니다. 받은 메일에만 있고, 보관기간이 지나면 `null` 입니다. 본문이 잘렸거나(`bodyTruncated`) 우리가 해석하지 못한 메일도 원문에는 전부 있습니다. ⚠️ 이 URL 은 **인증을 거치지 않습니다** — 가진 사람은 누구나 받을 수 있으니 로그나 외부에 남기지 마십시오. 그리고 **저장해 두고 나중에 쓰지 마십시오**: `expiresAt` 이후에는 동작하지 않습니다.

- `downloadUrl` (string, required)
- `expiresAt` (datetime, required) — 이 링크가 만료되는 시각.

## Examples

**Response**

```json
{
  "emailId": "clx9eml00001",
  "direction": "inbound",
  "status": "sent",
  "from": "no-reply@example.com",
  "to": [
    "customer@gmail.com"
  ],
  "recipientCount": 1,
  "createdAt": "2026-09-03T07:20:00.000Z",
  "bodyTruncated": false,
  "cc": [],
  "bcc": [],
  "suppressed": [],
  "subject": "주문이 접수되었습니다",
  "attachmentCount": 1,
  "failureCode": null,
  "failureMessage": null,
  "replyTo": [
    "support@example.com"
  ],
  "messageId": "CAF7n8xW1p_abc@mail.gmail.com",
  "inReplyTo": null,
  "references": [
    "CAF7n8xW1p_xyz@mail.gmail.com",
    "CAF7n8xW1p_abc@mail.gmail.com"
  ],
  "recipients": [
    "support@example.com"
  ],
  "receivedAt": "2026-09-04T05:48:00.000Z",
  "sizeBytes": 4030,
  "verdicts": {
    "spam": "PASS",
    "virus": "PASS",
    "spf": "PASS",
    "dkim": "PASS",
    "dmarc": "GRAY"
  },
  "parseStatus": "parsed",
  "text": "재고 문의드립니다. 언제쯤 입고될까요?",
  "html": "<p>재고 문의드립니다…</p>",
  "attachments": [
    {
      "attachmentId": "clx9att00001",
      "sizeBytes": 182034,
      "filename": "주문서.pdf",
      "contentType": "application/pdf",
      "contentId": "logo@example.com",
      "inline": false,
      "downloadUrl": "string",
      "expiresAt": "2026-09-04T07:15:00.000Z"
    }
  ],
  "raw": {
    "downloadUrl": "string",
    "expiresAt": "2026-09-04T07:15:00.000Z"
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/emails/clx9eml00001"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/emails/clx9eml00001';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/emails/clx9eml00001"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/emails/clx9eml00001")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/emails/clx9eml00001")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/emails/clx9eml00001', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/emails/clx9eml00001");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/emails/clx9eml00001")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```