API 사용하기
앱인토스 API를 사용하려면 mTLS 기반의 서버 간(Server-to-Server) 통신 설정이 반드시 필요해요.
이 문서에서는 mTLS 인증서 발급 방법부터 방화벽 설정, API 공통 규격까지 순서대로 안내해요.
아래 기능은 반드시 mTLS 인증서를 통한 통신이 필요해요
통신 구조
앱인토스 API는 파트너사 서버에서 앱인토스 서버로 요청을 전송하고,
앱인토스 서버가 토스 서버에 연동 요청을 전달하는 구조로 동작해요.


mTLS 인증서 발급 방법
서버용 mTLS 인증서는 콘솔에서 직접 발급할 수 있어요.
1. 앱 선택하기
앱인토스 콘솔에 접속해 인증서를 발급받을 앱을 선택하세요.
왼쪽 메뉴에서 mTLS 인증서 탭을 클릭한 뒤, + 발급받기 버튼을 눌러 발급을 진행해요.

2. 인증서 다운로드 및 보관
mTLS 인증서가 발급되면 인증서 파일과 키 파일을 다운로드할 수 있어요.
보관 시 주의해 주세요
- 인증서와 키 파일은 유출되지 않도록 안전한 위치에 보관해 주세요.
- 인증서가 만료되기 전에 반드시 재발급해 주세요.

콘솔에서 발급된 인증서는 아래와 같이 확인할 수 있어요.

인증서는 일반적으로 하나만 사용하지만, 무중단 교체를 위해 두 개 이상 등록해 둘 수도 있어요.
콘솔에서는 이를 위해 다중 인증서 관리 기능을 제공해요.
API 요청 시 인증서 설정
앱인토스 서버에 요청하려면 발급받은 인증서/키 파일을 서버 애플리케이션에 등록해야 해요.
아래는 주요 언어별 mTLS 요청 예제예요. 환경에 맞게 경로, 알고리즘, TLS 버전 등을 조정해 주세요.
Kotlin 예제
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.security.KeyFactory
import java.security.spec.PKCS8EncodedKeySpec
import java.io.FileReader
import java.io.ByteArrayInputStream
import java.util.Base64
import javax.net.ssl.*
class TLSClient {
fun createSSLContext(certPath: String, keyPath: String): SSLContext {
val cert = loadCertificate(certPath)
val key = loadPrivateKey(keyPath)
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
keyStore.load(null, null)
keyStore.setCertificateEntry("client-cert", cert)
keyStore.setKeyEntry("client-key", key, "".toCharArray(), arrayOf(cert))
val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmf.init(keyStore, "".toCharArray())
return SSLContext.getInstance("TLS").apply {
init(kmf.keyManagers, null, null)
}
}
private fun loadCertificate(path: String): X509Certificate {
val content = FileReader(path).readText()
.replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----", "")
.replace("\\s".toRegex(), "")
val bytes = Base64.getDecoder().decode(content)
return CertificateFactory.getInstance("X.509")
.generateCertificate(ByteArrayInputStream(bytes)) as X509Certificate
}
private fun loadPrivateKey(path: String): java.security.PrivateKey {
val content = FileReader(path).readText()
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replace("\\s".toRegex(), "")
val bytes = Base64.getDecoder().decode(content)
val spec = PKCS8EncodedKeySpec(bytes)
return KeyFactory.getInstance("RSA").generatePrivate(spec)
}
fun makeRequest(url: String, context: SSLContext): String {
val connection = (URL(url).openConnection() as HttpsURLConnection).apply {
sslSocketFactory = context.socketFactory
requestMethod = "GET"
connectTimeout = 5000
readTimeout = 5000
}
return connection.inputStream.bufferedReader().use { it.readText() }.also {
connection.disconnect()
}
}
}
fun main() {
val client = TLSClient()
val context = client.createSSLContext("/path/to/client-cert.pem", "/path/to/client-key.pem")
val response = client.makeRequest("https://apps-in-toss-api.toss.im/endpoint", context)
println(response)
}Python 예제
import requests
class TLSClient:
def __init__(self, cert_path, key_path):
self.cert_path = cert_path
self.key_path = key_path
def make_request(self, url):
response = requests.get(
url,
cert=(self.cert_path, self.key_path),
headers={'Content-Type': 'application/json'}
)
return response.text
if __name__ == '__main__':
client = TLSClient(
cert_path='/path/to/client-cert.pem',
key_path='/path/to/client-key.pem'
)
result = client.make_request('https://apps-in-toss-api.toss.im/endpoint')
print(result)JavaScript(Node.js) 예제
const https = require('https');
const fs = require('fs');
const options = {
cert: fs.readFileSync('/path/to/client-cert.pem'),
key: fs.readFileSync('/path/to/client-key.pem'),
rejectUnauthorized: true,
};
const req = https.request('https://apps-in-toss-api.toss.im/endpoint', { method: 'GET', ...options }, (res) => {
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
console.log('Response:', data);
});
});
req.on('error', (e) => console.error(e));
req.end();C# 예제
using System;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
class Program {
static async Task Main(string[] args) {
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(
new X509Certificate2("/path/to/client-cert.pem")
);
using var client = new HttpClient(handler);
var response = await client.GetAsync("https://apps-in-toss-api.toss.im/endpoint");
string body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
}C++ 예제(libcurl 사용)
#include <curl/curl.h>
#include <iostream>
#include <string>
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
userp->append((char*)contents, size * nmemb);
return size * nmemb;
}
int main() {
CURL* curl = curl_easy_init();
if (curl) {
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "https://apps-in-toss-api.toss.im/endpoint");
curl_easy_setopt(curl, CURLOPT_SSLCERT, "/path/to/client-cert.pem");
curl_easy_setopt(curl, CURLOPT_SSLKEY, "/path/to/client-key.pem");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
CURLcode res = curl_easy_perform(curl);
if (res == CURLE_OK) {
std::cout << "Response: " << response << std::endl;
} else {
std::cerr << "Error: " << curl_easy_strerror(res) << std::endl;
}
curl_easy_cleanup(curl);
}
return 0;
}통신 방화벽 확인하기
서버에서 Inbound, Outbound 방화벽을 관리하고 있다면 아래 IP와 포트를 반드시 허용해야 해요.
허용하지 않으면 API 호출이 실패하거나 콜백을 받지 못해요.
가맹점이 허용해야 하는 Inbound IP
앱인토스 → 가맹점
앱인토스가 가맹점 서버로 요청을 보낼 때 사용하는 IP예요.
예를 들어 결제 결과 콜백을 받을 때 필요해요.
| IP | Port |
가맹점이 허용해야 하는 Outbound IP
가맹점 → 앱인토스
| 기능 | 도메인 | IP | Port |
|---|---|---|---|
| 토스 로그인, 스마트 발송, 프로모션(토스 포인트) | apps-in-toss-api.toss.im | 117.52.3.192, 211.115.96.192, 106.249.5.192 | 443 |
| 토스 페이 | pay-apps-in-toss-api.toss.im | 117.52.3.195, 211.115.96.195, 106.249.5.195 | 443 |
API 공통 규격
도메인 정보
https://apps-in-toss-api.toss.imhttps://pay-apps-in-toss-api.toss.im
공통 응답 형식
모든 API는 공통된 응답 구조를 사용해요.resultType 값으로 성공 여부를 먼저 확인하세요.
성공 응답
{
"resultType": "SUCCESS",
"success": {
"sample": "data"
}
}resultType이"SUCCESS"이면 요청이 정상 처리된 상태예요.- 실제 응답 데이터는
success객체 안에 있어요. - 각 API마다
success내부 구조는 달라요.
실패 응답
{
"resultType": "FAIL",
"error": {
"errorCode": "INVALID_PARAMETER",
"reason": "요청에 실패했습니다."
}
}resultType이"FAIL"이면 요청 처리에 실패한 상태예요.errorCode는 오류 유형을 나타내는 코드예요.reason에는 사람이 읽을 수 있는 오류 설명이 들어 있어요.
응답을 처리할 때는 반드시 resultType을 먼저 검사한 뒤, 성공과 실패 로직을 나눠 구현해 주세요.
요청 제한 정책
앱인토스 API는 안정적인 서비스 운영을 위해 요청 수를 제한해요.
- 미니앱 기준 분당 최대 3,000 QPM(Queries Per Minute)까지 요청할 수 있어요.
- 이 한도를 초과하면 일정 시간 동안 추가 요청이 차단될 수 있어요.
기본 3,000 QPM보다 많은 요청이 필요하다면 채널톡으로 상향을 요청할 수 있어요.
요청 시 사용 목적, 예상 트래픽 규모, 피크 시간대 요청량을 함께 전달해 주세요.
대량 트래픽이 예상된다면 서비스 오픈 전에 미리 협의하는 것이 좋아요.
자주 묻는 질문
mTLS 미적용 상태에서 API를 호출하면 발생해요.
간편 로그인·결제·광고 등 API 기능을 쓰기 전에 서버에 mTLS를 설정한 뒤 호출해 주세요.
인증서/키는 서버 인증에 직접 사용돼요.
노출될 경우 타 파트너가 인증서를 도용해 API를 호출하거나, 의도치 않은 포인트 지급 등 사고가 날 수 있어요.
즉시 해당 인증서를 폐기(삭제) 후 재발급해 주세요.
mTLS 인증서는 390일 동안 유효해요.
만료되면 인증이 실패하고 서버 간 통신이 중단돼요.
콘솔에서 미리 새 인증서를 발급받고 교체해 주세요.
추후에는 인증서 만료 1개월 전, 1주일 전, 1일 전에 이메일로 안내할 예정이에요.
제한 없이 여러 개 발급 가능해요.
대부분 한 개를 쓰지만, 무중단 교체를 위해 2개 이상을 병행 등록하기도 해요.
이를 위해 콘솔에서 다중 인증서 관리를 지원해요.