Airtable 커넥터
Airtable 베이스를 Brevo에 연결하여 CRM 동기화, 제품 카탈로그 관리, 그리고 Tajo를 통해 구조화된 데이터 기반의 자동화된 마케팅 워크플로우를 구현하세요.
개요
| 속성 | 값 |
|---|---|
| 플랫폼 | Airtable |
| 카테고리 | Custom |
| 설정 난이도 | 쉬움 |
| 공식 통합 | 아니오 |
| 동기화되는 데이터 | 레코드, 테이블, 사용자 |
| API 유형 | REST API |
| 인증 | Personal Access Token / OAuth 2.0 |
| Base URL | https://api.airtable.com/v0/ |
주요 기능
- 테이블-리스트 동기화 - Airtable 테이블 레코드를 Brevo 연락처 리스트로 직접 동기화합니다
- 제품 카탈로그 브릿지 - Airtable 테이블을 이메일 추천용 제품 카탈로그로 활용합니다
- CRM 동기화 - Airtable CRM과 Brevo 연락처 간의 양방향 동기화
- 폼 제출 이벤트 - Airtable 폼 제출을 Brevo 이벤트로 전달합니다
- 뷰 기반 필터링 - 특정 Airtable 뷰를 타깃 Brevo 리스트로 동기화합니다
- 웹훅 자동화 - Airtable 레코드 변경 시 Brevo 캠페인을 트리거합니다
사전 준비 사항
시작하기 전에 다음 사항이 준비되어 있어야 합니다.
- Airtable 계정 (Free 플랜 이상)
- Personal Access Token 또는 구성된 OAuth app
- 동기화하려는 베이스 및 테이블에 대한 액세스 권한
- API 액세스가 활성화된 Brevo 계정
- 활성 구독이 있는 Tajo 계정
인증
Airtable은 Personal Access Token과 OAuth 2.0을 지원합니다.
옵션 1: Personal Access Token (권장)
- airtable.com/create/tokens로 이동합니다
- Create new token을 클릭합니다
- 이름을 “Tajo Integration”으로 지정합니다
- 스코프를 추가합니다.
data.records:readdata.records:writedata.recordComments:readschema.bases:readwebhook:manage- 특정 베이스 또는 모든 베이스에 대한 액세스를 추가합니다
- Create token을 클릭합니다
옵션 2: OAuth 2.0
멀티 유저 통합의 경우 OAuth 2.0 플로우를 사용하세요.
- airtable.com/create/oauth에서 통합을 등록합니다
- 리디렉션 URI를 구성합니다:
https://app.tajo.io/callbacks/airtable - 위와 동일한 스코프를 요청합니다
토큰 스코핑
Personal Access Token은 특정 베이스로 범위를 제한할 수 있습니다. 보안을 위해 “All current and future bases”를 선택하지 말고 통합에 필요한 베이스에만 액세스를 부여하세요.
Tajo에 연결
tajo connectors install airtable \ --token $AIRTABLE_TOKEN설정
기본 설정
connectors: airtable: enabled: true
sync: records: true comments: false
tables: - base_id: "appXXXXXXXXXXXXXX" table_name: "Customers" view: "Active Customers" sync_to_list: 28 - base_id: "appXXXXXXXXXXXXXX" table_name: "Products" sync_as: "catalog"필드 매핑
Airtable 필드를 Brevo 연락처 속성에 매핑합니다.
field_mapping: # Airtable field -> Brevo attribute Name: FIRSTNAME Email: email Phone: SMS Company: COMPANY Status: LEAD_STATUS Revenue: TOTAL_REVENUE "Last Contact": LAST_CONTACT_DATE Tags: TAGS Notes: NOTES "Created Time": SIGNUP_DATE뷰 기반 동기화
views: - base_id: "appXXXXXXXXXXXXXX" table_name: "Customers" view: "High Value" sync_to_list: 29 filter_by_view: true
- base_id: "appXXXXXXXXXXXXXX" table_name: "Customers" view: "Churned" sync_to_list: 30 filter_by_view: trueAPI 엔드포인트
Tajo는 다음 Airtable Web API 엔드포인트와 통합됩니다.
| 엔드포인트 | Method | 용도 |
|---|---|---|
/v0/{baseId}/{tableIdOrName} | GET | 테이블의 레코드 목록 조회 |
/v0/{baseId}/{tableIdOrName} | POST | 레코드 생성 |
/v0/{baseId}/{tableIdOrName} | PATCH | 레코드 업데이트 |
/v0/{baseId}/{tableIdOrName} | DELETE | 레코드 삭제 |
/v0/{baseId}/{tableIdOrName}/{recordId} | GET | 단일 레코드 조회 |
/v0/meta/bases | GET | 액세스 가능한 베이스 목록 |
/v0/meta/bases/{baseId}/tables | GET | 베이스의 테이블 목록 |
/v0/{baseId}/{tableIdOrName}/listRecordComments | GET | 레코드 코멘트 목록 |
/v0/bases/{baseId}/webhooks | POST | 웹훅 생성 |
/v0/bases/{baseId}/webhooks | GET | 웹훅 목록 |
코드 예제
커넥터 초기화
import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({ apiKey: process.env.TAJO_API_KEY, brevoApiKey: process.env.BREVO_API_KEY});
await tajo.connectors.connect('airtable', { token: process.env.AIRTABLE_TOKEN});테이블을 Brevo로 동기화
// Sync an Airtable table to a Brevo listawait tajo.connectors.sync('airtable', { type: 'full', resources: ['records'], baseId: 'appXXXXXXXXXXXXXX', tableName: 'Customers', view: 'Active Customers', targetList: 28});
const status = await tajo.connectors.status('airtable');console.log(status);// {// connected: true,// lastSync: '2024-03-15T15:00:00Z',// recordsSynced: 2340,// tablesMonitored: 2,// basesConnected: 1// }웹훅 처리
// Airtable webhooks notify of changes; fetch details with cursorapp.post('/webhooks/airtable', async (req, res) => { const { base, webhook, timestamp } = req.body;
// Fetch changed records using webhook cursor const changes = await tajo.connectors.getWebhookPayloads('airtable', { baseId: base.id, webhookId: webhook.id, cursor: timestamp });
for (const change of changes) { await tajo.connectors.handleEvent('airtable', { type: change.actionType, payload: change }); }
res.status(200).send('OK');});Brevo에서 레코드 생성
// Create an Airtable record when a Brevo contact convertstajo.events.on('contact.attribute_updated', async (event) => { if (event.attribute === 'LIFECYCLE_STAGE' && event.value === 'customer') { await tajo.connectors.create('airtable', { baseId: 'appXXXXXXXXXXXXXX', tableName: 'Customers', fields: { Name: event.contact.name, Email: event.contact.email, Status: 'Customer', 'Converted Date': new Date().toISOString().split('T')[0] } }); }});요청 제한
Airtable은 베이스별로 요청 제한을 적용합니다.
| 제한 유형 | 값 |
|---|---|
| API 요청 제한 | 베이스당 초당 5회 |
| 요청당 레코드 | 최대 100개 (list), 최대 10개 (create/update) |
| 웹훅 페이로드 | listWebhookPayloads 호출당 50개 페이로드 |
| 요청 크기 | 페이로드 최대 ~2MB |
배치 작업
Airtable은 요청당 최대 10개의 레코드를 생성하거나 업데이트할 수 있습니다. Tajo는 더 큰 작업을 자동으로 여러 요청으로 배치 처리하면서 요청 제한을 준수합니다.
문제 해결
일반적인 문제
| 문제 | 원인 | 해결 방법 |
|---|---|---|
| 401 Unauthorized | 유효하지 않거나 만료된 토큰 | Personal Access Token을 재생성하세요 |
| 403 Forbidden | 토큰에 베이스 액세스 권한이 없음 | 토큰의 스코프에 베이스를 추가하세요 |
| 404 Not Found | 유효하지 않은 베이스 또는 테이블 ID | 베이스 ID와 테이블 이름을 확인하세요 |
| 422 Invalid Request | 필드 타입 불일치 | Airtable 필드 타입이 데이터와 일치하는지 확인하세요 |
| Rate limit exceeded | 베이스당 초당 5회 초과 | 동기화 빈도를 낮추거나 베이스 동기화를 분산하세요 |
디버그 모드
connectors: airtable: debug: true log_level: verbose log_api_calls: true연결 테스트
tajo connectors test airtable# ✓ API authentication successful# ✓ Base access verified# ✓ Table schema readable# ✓ Record listing operational# ✓ Webhook registration available모범 사례
- 토큰을 특정 베이스로 스코프 제한 - 필요한 경우가 아니면 모든 베이스에 액세스를 부여하지 마세요
- 필터링된 동기화에 뷰 사용 - 데이터 볼륨을 줄이기 위해 전체 테이블 대신 특정 뷰를 동기화하세요
- 레코드 작업을 배치 처리 - 생성 및 업데이트를 10개씩 배치로 그룹화하세요
- 페이지네이션 처리 - Airtable은 페이지당 100개 레코드를 반환합니다.
offset으로 순회하세요 - 실시간을 위해 웹훅 사용 - 변경 사항을 폴링하는 대신 웹훅을 등록하세요
- 필드 타입을 정확히 매핑 - Airtable 필드 타입(select, number, date)을 Brevo 속성 타입과 일치시키세요
보안
- Personal Access Token - 특정 베이스 및 작업으로 범위 제한
- OAuth 2.0 - 리프레시 토큰이 포함된 안전한 인증 플로우
- HTTPS 전용 - 모든 API 통신은 TLS 1.2+를 통해 암호화됩니다
- 베이스 수준 액세스 제어 - 토큰이 개별 베이스로 스코프 제한됩니다
- 암호화된 저장소 - 토큰은 Tajo에 저장 시 암호화됩니다
- 웹훅 HMAC 검증 - 웹훅 알림의 진위성을 검증합니다