Skip to content

Repository files navigation

LoftBox .NET SDK

AI 에이전트를 위한 API-first 이메일 인프라 LoftBox 의 공식 .NET SDK. System.Net.Http.HttpClient + System.Text.Json 만 사용하며 ASP.NET Core DI 통합을 제공합니다.

설치

dotnet add package LoftBox.Sdk

대상 프레임워크: net8.0.

빠른 시작 (plain)

using LoftBox.Sdk;

using var client = new LoftBoxClient("lb_live_xxx");

// 에이전트 + 메일박스
var agent = await client.Agents.CreateAsync(new AgentCreateParams
{
    Name = "Support Bot",
    Slug = "support-bot",
});

var mailbox = await client.Mailboxes.CreateAsync(agent.Id, new MailboxCreateParams
{
    LocalPart = "support",
});

// 발송 (멱등 키로 중복 방지)
var msg = await client.Messages.SendAsync(new SendMessageParams
{
    MailboxId = mailbox.Id,
    To = new[] { "recipient@example.com" },
    Subject = "Hello",
    BodyText = "World",
    IdempotencyKey = "welcome-42",
});

// 수신 폴링 → ack
var inbox = await client.Mailboxes.ListInboxAsync(mailbox.Id);
var ids = inbox.Data.Select(m => m.Id).ToArray();
await client.Mailboxes.AckInboxAsync(mailbox.Id, ids);

모든 요청 메서드는 마지막 인자로 CancellationToken 을 받습니다(취소·타임아웃 제어).

빠른 시작 (ASP.NET Core DI)

AddLoftBoxIHttpClientFactory 가 관리하는 HttpClient 를 사용해 LoftBoxClient 를 싱글톤으로 등록합니다(소켓 고갈 없이 안전 재사용).

// Program.cs
builder.Services.AddLoftBox(options =>
{
    options.ApiKey = builder.Configuration["LoftBox:ApiKey"]!;
    // options.BaseUrl = "https://api.loftbox.net"; // 기본값
    // options.Timeout = TimeSpan.FromSeconds(30);  // 기본값
});
// 컨트롤러/서비스에 주입
public sealed class NotifyService(LoftBoxClient loftbox)
{
    public Task SendAsync(string mailboxId, string to, CancellationToken ct) =>
        loftbox.Messages.SendAsync(new SendMessageParams
        {
            MailboxId = mailboxId,
            To = new[] { to },
            Subject = "안녕하세요",
            BodyMarkdown = "**LoftBox** 에서 보냅니다.",
        }, ct);
}

설정

// API 키만
using var c1 = new LoftBoxClient("lb_live_xxx");

// baseUrl / timeout 지정
using var c2 = new LoftBoxClient("lb_live_xxx",
    baseUrl: "https://api.loftbox.net", // 기본값
    timeout: TimeSpan.FromSeconds(30));  // 기본값

// 커스텀 HttpClient 주입(프록시/테스트) — 타임아웃은 그 클라이언트가 통제
using var c3 = new LoftBoxClient(
    new LoftBoxClientOptions { ApiKey = "lb_live_xxx" },
    httpClient: myHttpClient);

ApiKey 는 필수입니다.

기능

  • 발송 Messages.SendAsync(...) — 텍스트/HTML/Markdown, 첨부, cc, 답장 헤더
  • 예약 발송 SendAsync(new SendMessageParams { ..., SendAt = "2030-01-01T09:00:00Z" })
  • 멱등 발송 SendAsync(new SendMessageParams { ..., IdempotencyKey = "..." })
  • 수신 Mailboxes.ListInboxAsync() + AckInboxAsync(), Message.ExtractedText(인용 제거 본문)
  • 라벨 Messages.AddLabelsAsync(), RemoveLabelAsync(), ListAsync(new ListMessagesParams { Label = ... })
  • 전문검색 Messages.ListAsync(new ListMessagesParams { Q }), Threads.ListAsync(new ListThreadsParams { Q })
  • 스레드 Threads.ListAsync(), ListMessagesAsync()
  • 승인 Messages.ApproveAsync(id, reason), RejectAsync(...)
  • 웹훅 Webhooks.CreateAsync(agentId, url, eventTypes)
  • 도메인 / suppression Domains.*, Suppressions.*
  • 인바운드 안전 (#369/#370) Message.InjectionScore / InjectionCategories(프롬프트-인젝션 신호, 차단 아님) + InboundRules.*(발신자 allow/block)
  • 첨부 Attachments.ListForMessageAsync(), PresignedUrlAsync()

인바운드 안전 (프롬프트-인젝션 신호 + 발신자 통제)

수신 메일은 임의 외부 발신자가 보낸 untrusted 입력입니다. 두 가지 통제를 제공합니다.

// #369: 수신 메시지마다 프롬프트-인젝션 휴리스틱 점수(0~1) + 발화 카테고리.
//       신호 전용 — LoftBox 는 차단하지 않으며, 에이전트가 판단합니다.
var inbox = await client.Mailboxes.ListInboxAsync("mb_xxx");
foreach (var msg in inbox.Data)
{
    if (msg.InjectionScore is >= 0.7)
    {
        // 예: 사람 승인 후에만 메일 내 지시를 따른다.
        RequireHumanReview(msg);
    }
    // msg.InjectionCategories: ["instruction_override", ...]
}

// #370: 발신자 allow/block 리스트로 수신 자체를 통제(SMTP 거부).
await client.InboundRules.CreateAsync(new InboundRuleCreateParams
{
    RuleType = "block", PatternType = "domain", Pattern = "evil.com",
});
await client.InboundRules.CreateAsync(new InboundRuleCreateParams
{
    RuleType = "allow", PatternType = "address", Pattern = "partner@trusted.com",
    MailboxId = "mb_xxx", // 미지정 시 org 전체
});
var rules = await client.InboundRules.ListAsync(new ListInboundRulesParams { MailboxId = "mb_xxx" });
await client.InboundRules.RemoveAsync("rule_id");

allow 리스트가 하나라도 있으면 미매치 발신자는 거부됩니다(화이트리스트). 평가는 위조 가능한 From 헤더가 아니라 SMTP envelope sender 로 합니다.

오류 처리

모든 API 에러는 LoftBoxException 을 상속합니다. 상태별 서브클래스로 분기하세요.

try
{
    await client.Messages.SendAsync(/* ... */);
}
catch (RateLimitException ex)
{
    if (ex.RetryAfterSecs is { } secs)
        Console.WriteLine($"{secs}s 후 재시도");
}
catch (NotFoundException ex)
{
    Console.WriteLine($"{ex.StatusCode} {ex.Message}");
}
catch (LoftBoxException ex)
{
    // 공통 필드: StatusCode / Message / RequestId / Body
    Console.WriteLine($"request={ex.RequestId} status={ex.StatusCode}");
}

에러 타입 매핑: 400/422 → ValidationException, 401 → AuthenticationException, 403 → PermissionException, 404 → NotFoundException, 409 → ConflictException, 429 → RateLimitException(RetryAfterSecs), 그 외 → LoftBoxException.

페이지네이션

목록 메서드는 Page<T>{ Data, NextCursor } 를 반환합니다(NextCursorstring?).

var page = await client.Messages.ListAsync(new ListMessagesParams { MailboxId = mb.Id, Limit = 50 });
while (true)
{
    foreach (var m in page.Data) { /* ... */ }
    if (page.NextCursor is null) break;
    page = await client.Messages.ListAsync(new ListMessagesParams
    {
        MailboxId = mb.Id, Limit = 50, Cursor = page.NextCursor,
    });
}

확장 필드 생존

응답 record 는 [JsonExtensionData] 를 두어 서버가 추가한 미지정 필드도 Extra 사전에 보존됩니다. SDK 업그레이드 없이 신규 필드를 읽을 수 있습니다.

예제

examples/Quickstart 참고(발송 + 수신 폴링).

라이선스

MIT

About

LoftBox .NET SDK — AI 에이전트용 이메일 인프라 (NuGet: LoftBox.Sdk)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages