폼 전송 API

폼 전송 및 전달 상태 추적을 위한 API 레퍼런스

폼 전송 API

폼 전송 API로 이메일이나 SMS를 통해 폼을 전송하고, 전송 상태와 응답 여부를 추적할 수 있습니다.

이 API는 특정 수신자에게 폼을 직접 보내고 싶을 때 사용합니다. 수신자별로 고유한 customerKey를 지정해 전송하면, 이후 전송 상태(전송됨/열람/응답 완료)를 추적할 수 있습니다. 수신자의 응답 데이터는 폼 응답 API로 조회하세요.

주의사항

폼 전송 API는 Walla와 사전에 협의된 경우에만 사용할 수 있습니다. 이 기능을 활성화하려면 Walla 지원팀에 문의하세요.

엔드포인트

수신자에게 폼 전송

하나 이상의 수신자에게 게시된 폼을 전송합니다.

POST /open-api/v1/forms/{formId}/delivery

참고: 게시된 폼만 전송할 수 있습니다. 게시되지 않은 폼은 404 오류가 반환됩니다.

매개변수

경로 매개변수

매개변수타입필수 여부설명
formIdstring고유한 폼 식별자

요청 본문

필드타입필수 여부설명
recipientsarray수신자 객체 배열 (최소 1개)
recipients[].customerKeystring고유한 고객 식별자
recipients[].emailstring아니오수신자 이메일 주소
recipients[].phoneNumberstring아니오수신자 전화번호
subjectstring아니오이메일 제목 (기본값: "설문이 도착했습니다.")
messagestring아니오전송 메시지 (기본값: "설문 링크를 확인하세요.")

참고: 각 수신자에는 이메일 또는 전화번호 중 최소 하나가 필요합니다.

요청

요청 예시

curl -X POST "https://api.walla.my/open-api/v1/forms/form_abc/delivery" \
  -H "X-WALLA-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "recipients": [
      {
        "customerKey": "customer_001",
        "email": "john@example.com",
        "phoneNumber": "+821012345678"
      },
      {
        "customerKey": "customer_002",
        "email": "jane@example.com"
      }
    ],
    "subject": "We value your feedback!",
    "message": "Please take a moment to complete our survey."
  }'

응답

성공 응답 (200 OK)

{
  "success": true,
  "data": {
    "totalRecipients": 2,
    "successfulDeliveries": 2,
    "failedDeliveries": []
  }
}

부분 성공 응답 (200 OK)

일부 수신자에게 전송이 실패한 경우:

{
  "success": true,
  "data": {
    "totalRecipients": 3,
    "successfulDeliveries": 2,
    "failedDeliveries": [
      {
        "customerKey": "customer_003",
        "email": "invalid-email",
        "phoneNumber": null,
        "error": "Invalid email format"
      }
    ]
  }
}

응답 필드

필드타입설명
successboolean요청 성공 여부
data.totalRecipientsnumber전체 수신자 수
data.successfulDeliveriesnumber성공적으로 전송된 수
data.failedDeliveriesarray오류 세부 정보가 포함된 실패한 전송 목록

오류 응답

  • 400 Bad Request: 잘못된 요청 데이터

    {
      "error": "Invalid request: recipients array is required"
    }
  • 403 Forbidden: 인증 실패 또는 권한 부족

    {
      "error": "Authentication failed or insufficient permissions"
    }
  • 404 Not Found: 폼을 찾을 수 없거나 게시되지 않음

    {
      "error": "Form not found or not published"
    }
  • 500 Internal Server Error: 서버 오류


모든 전송 수신자 조회

특정 폼의 전체 수신자 목록을 반환합니다.

GET /open-api/v1/forms/{formId}/delivery

매개변수

경로 매개변수

매개변수타입필수 여부설명
formIdstring고유한 폼 식별자

요청

요청 예시

curl -X GET "https://api.walla.my/open-api/v1/forms/form_abc/delivery" \
  -H "X-WALLA-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json"

응답

성공 응답 (200 OK)

{
  "success": true,
  "data": [
    {
      "id": "delivery_123",
      "formId": "form_abc",
      "customerKey": "customer_001",
      "email": "john@example.com",
      "phone": "+821012345678",
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T11:45:00Z"
    },
    {
      "id": "delivery_456",
      "formId": "form_abc",
      "customerKey": "customer_002",
      "email": "jane@example.com",
      "phone": null,
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ]
}

오류 응답

  • 403 Forbidden: 인증 실패 또는 권한 부족
  • 404 Not Found: 폼을 찾을 수 없거나 게시되지 않음
  • 500 Internal Server Error: 서버 오류

특정 수신자의 전송 상태 조회

특정 수신자의 전송 상태와 응답 여부를 확인합니다.

POST /open-api/v1/forms/{formId}/delivery/status

매개변수

경로 매개변수

매개변수타입필수 여부설명
formIdstring고유한 폼 식별자

요청 본문

필드타입필수 여부설명
customerKeysarray확인할 고객 키 배열 (최소 1개)

요청

요청 예시

curl -X POST "https://api.walla.my/open-api/v1/forms/form_abc/delivery/status" \
  -H "X-WALLA-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "customerKeys": ["customer_001", "customer_002", "customer_003"]
  }'

응답

성공 응답 (200 OK)

{
  "success": true,
  "data": [
    {
      "id": "delivery_123",
      "formId": "form_abc",
      "customerKey": "customer_001",
      "email": "john@example.com",
      "phone": "+821012345678",
      "status": "RESPONDED",
      "lastSentAt": "2024-01-15T10:30:00Z",
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T14:20:00Z"
    },
    {
      "id": "delivery_456",
      "formId": "form_abc",
      "customerKey": "customer_002",
      "email": "jane@example.com",
      "phone": null,
      "status": "OPENED",
      "lastSentAt": "2024-01-15T10:30:00Z",
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T11:15:00Z"
    },
    {
      "id": "delivery_789",
      "formId": "form_abc",
      "customerKey": "customer_003",
      "email": "bob@example.com",
      "phone": null,
      "status": "SENT",
      "lastSentAt": "2024-01-15T10:30:00Z",
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ]
}

응답 필드

필드타입설명
successboolean요청 성공 여부
dataarray전송 상태 객체 배열
data[].idstring전송 식별자
data[].formIdstring폼 식별자
data[].customerKeystring고객 식별자
data[].emailstring수신자 이메일
data[].phonestring수신자 전화번호 (요청 시 phoneNumber로 전송, 응답에서는 phone으로 반환)
data[].statusstring전송 상태 (아래 참조)
data[].lastSentAtstring (date-time)마지막 전송 타임스탬프
data[].createdAtstring (date-time)최초 전송 생성 타임스탬프
data[].updatedAtstring (date-time)마지막 업데이트 타임스탬프

전송 상태 값

상태설명
NOT_SENT전송이 생성되었지만 아직 전송되지 않음
SENT폼이 성공적으로 전달됨
OPENED수신자가 폼을 열람함
RESPONDED수신자가 폼을 작성하고 제출함

오류 응답

  • 400 Bad Request: 잘못된 요청 (예: customerKeys 배열이 비어있음)
  • 403 Forbidden: 인증 실패 또는 권한 부족
  • 404 Not Found: 폼을 찾을 수 없거나 게시되지 않음
  • 500 Internal Server Error: 서버 오류

코드 예제

JavaScript/TypeScript

interface Recipient {
  customerKey: string;
  email?: string;
  phoneNumber?: string;
}

interface DeliveryOptions {
  subject?: string;
  message?: string;
}

async function sendForm(
  formId: string,
  recipients: Recipient[],
  options?: DeliveryOptions
) {
  const response = await fetch(
    `https://api.walla.my/open-api/v1/forms/${formId}/delivery`,
    {
      method: 'POST',
      headers: {
        'X-WALLA-API-KEY': process.env.WALLA_API_KEY!,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        recipients,
        subject: options?.subject || '설문이 도착했습니다.',
        message: options?.message || '설문 링크를 확인하세요.'
      })
    }
  );

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  return await response.json();
}

async function getDeliveryStatus(formId: string, customerKeys: string[]) {
  const response = await fetch(
    `https://api.walla.my/open-api/v1/forms/${formId}/delivery/status`,
    {
      method: 'POST',
      headers: {
        'X-WALLA-API-KEY': process.env.WALLA_API_KEY!,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ customerKeys })
    }
  );

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  const data = await response.json();
  return data.data;
}

// Usage example
const result = await sendForm('form_abc', [
  {
    customerKey: 'customer_001',
    email: 'john@example.com'
  },
  {
    customerKey: 'customer_002',
    email: 'jane@example.com',
    phoneNumber: '+821012345678'
  }
], {
  subject: 'We need your feedback!',
  message: 'Please help us improve our services.'
});

console.log(`Successfully sent to ${result.data.successfulDeliveries} recipients`);

// Check status
const statuses = await getDeliveryStatus('form_abc', ['customer_001', 'customer_002']);
statuses.forEach(status => {
  console.log(`${status.customerKey}: ${status.status}`);
});

Python

import requests
import os
from typing import List, Dict, Any, Optional

class WallaDeliveryAPI:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.walla.my/open-api/v1"
        self.headers = {
            "X-WALLA-API-KEY": api_key,
            "Content-Type": "application/json"
        }

    def send_form(
        self,
        form_id: str,
        recipients: List[Dict[str, str]],
        subject: Optional[str] = None,
        message: Optional[str] = None
    ) -> Dict[str, Any]:
        """Send form to recipients"""
        url = f"{self.base_url}/forms/{form_id}/delivery"
        payload = {
            "recipients": recipients,
            "subject": subject or "설문이 도착했습니다.",
            "message": message or "설문 링크를 확인하세요."
        }

        response = requests.post(url, headers=self.headers, json=payload)
        response.raise_for_status()
        return response.json()

    def get_all_deliveries(self, form_id: str) -> List[Dict[str, Any]]:
        """Get all delivery recipients for a form"""
        url = f"{self.base_url}/forms/{form_id}/delivery"
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()["data"]

    def get_delivery_status(
        self,
        form_id: str,
        customer_keys: List[str]
    ) -> List[Dict[str, Any]]:
        """Get delivery status for specific recipients"""
        url = f"{self.base_url}/forms/{form_id}/delivery/status"
        payload = {"customerKeys": customer_keys}

        response = requests.post(url, headers=self.headers, json=payload)
        response.raise_for_status()
        return response.json()["data"]

    def get_response_rate(self, form_id: str) -> float:
        """Calculate response rate for a form"""
        deliveries = self.get_all_deliveries(form_id)
        if not deliveries:
            return 0.0

        customer_keys = [d["customerKey"] for d in deliveries]
        statuses = self.get_delivery_status(form_id, customer_keys)

        responded = sum(1 for s in statuses if s["status"] == "RESPONDED")
        return (responded / len(statuses)) * 100

# Usage
api = WallaDeliveryAPI(os.getenv("WALLA_API_KEY"))

# Send form
result = api.send_form(
    "form_abc",
    [
        {"customerKey": "customer_001", "email": "john@example.com"},
        {"customerKey": "customer_002", "email": "jane@example.com"}
    ],
    subject="Your feedback matters!",
    message="Please take 2 minutes to complete our survey."
)

print(f"Sent to {result['data']['successfulDeliveries']} recipients")

# Check response rate
response_rate = api.get_response_rate("form_abc")
print(f"Response rate: {response_rate:.2f}%")

모범 사례

고객 키 관리

  • 시스템 전체에서 일관된 고객 키를 사용하세요
  • 고객 키는 고객별로 고유해야 합니다
  • 내부 고객 ID를 고객 키로 쓰는 것도 좋은 방법입니다
  • 고객 키와 내부 시스템 간의 매핑을 기록해 두세요

전송 최적화

  • 일괄 처리: 한 번의 요청으로 여러 수신자에게 보내세요
  • 오류 처리: failedDeliveries 배열을 항상 확인하고, 실패 건은 재시도하세요
  • 상태 추적: 주기적으로 전송 상태를 확인해서 참여도를 파악하세요
  • 속도 제한: API 키당 시간당 6,000건의 속도 제한이 적용됩니다

제목 및 메시지 맞춤화

// 캠페인에 따른 맞춤화
const campaigns = {
  feedback: {
    subject: 'We value your feedback!',
    message: 'Help us improve our services by completing this quick survey.'
  },
  nps: {
    subject: 'How likely are you to recommend us?',
    message: 'Your opinion matters. Please share your thoughts.'
  }
};

await sendForm(formId, recipients, campaigns.feedback);

다음 단계

목차