대시보드
불러오는 중이에요.
소유자 인증하기
메타모아에 올라와 있는 공간의 공간운영자라면 소유를 확인하고 공간 정보·정각 모임·소식을 직접 관리할 수 있어요. 공간 주소나 이름으로 찾아 주세요. 아직 없는 공간은 등록 요청으로 올려 주세요.
키 발급
소식·접속자 수를 보내거나, 이 페이지에서 고치는 이름·소개·대표 이미지·링크를 프로그램으로 고치려면 키가 필요해요. 쓰는 법은 API 안내에 있어요. 키는 여러 개 둘 수 있어요. 추가로 발급해도 예전 키는 그대로 쓰이고, 「멈추기」를 누른 키만 바로 멈춰요.
이 키는 지금 한 번만 보여요. 안전한 곳에 보관해 주세요.
사이트로 소유 확인
아래 셋 중 하나를 사이트에 넣고 「확인하기」를 눌러 주세요. 네이버 서치어드바이저·구글 서치 콘솔과 같은 방식이에요. 코드는 이 브라우저에 묶여 있으니 넣은 뒤 이 화면에서 「확인하기」를 눌러 주세요. 확인되면 메일 없이 바로 「공간 관리」로 넘어가요. 넣은 것을 지우면 며칠 뒤 공간운영자 권한이 풀려요.
이 공간은 받아 둔 공간운영자 메일이 없어요. 메일 대신 아래 셋 중 하나로 사이트에서 확인해 주세요.
- 메타 태그 공간 페이지나 사이트 첫 화면의 <head> 안에 넣기
- HTML 파일
이 파일을 사이트 맨 위 폴더에 올리고 이 열리면 확인하기 - DNS TXT 레코드 에 TXT 레코드로 넣기
메일로 확인
이 공간을 운영하고 있다면 저희가 받아 둔 공간운영자 메일 주소를 넣어 주세요. 주소가 맞으면 확인 링크를 보내 드려요. 링크를 누르기 전에는 아무것도 바뀌지 않아요.
메일이 없거나 다르다면
스레드 @float_1122에 DM으로 이메일 주소를 보내 주시면 확인 후 연결해 드려요.
내 공간
공간 열기- 지금 접속자
- —
- 광고 상태
- —
- 이번 달 수익
- —
처리할 일
- 지금 처리할 일이 없어요.
제작자 커뮤니티 최근 글
전체 보기- 불러오는 중이에요.
대시보드 로그인용 메일 등록
넣으면 다른 기기에서도 이 메일로 로그인(인증번호·Google)해서 대시보드로 바로 들어와요. 넣지 않아도 이 브라우저에서는 그대로 관리할 수 있어요.
저장하면 확인 메일 없이 바로 이 공간의 공간운영자 메일이 돼요.
접속자 API 연결
위 공간 키로 지금 접속자 수를 10분에 한 번 보내면 랭킹과 첫 화면 합계에 반영돼요. 키는 서버에서만 쓰고 브라우저 코드에는 넣지 마세요. 업데이트 소식·이벤트·정보 수정도 같은 키로 돼요. 자세한 건 API 안내에 있어요.
포탈 연결
다른 공간과 서로 오가는 포탈은 공간운영자끼리 맞춰서 연결해요. 연결할 곳은 제작자 커뮤니티의 「공간연동」에서 찾아요.
서로 수락(매치)하면 두 공간에 포탈이 켜지고, 한쪽이 끊으면 양쪽 다 꺼져요. 넣는 법: 포탈 키트 · 포탈 API
메타모아에 올라간 공간의 공간운영자끼리 공간연동(포탈)·구인·협업 이야기를 나누는 곳이에요. 대시보드에 로그인한 그대로 읽고 쓸 수 있어요.
불러오는 중이에요.
로그인이 필요해요
대시보드에 로그인하면 바로 들어와요. 메타모아 계정으로도 들어올 수 있어요(공간운영자 메일로 로그인한 계정).
아직 확인된 공간운영자가 아니에요
목록에 오른 공간의 공간운영자로 확인돼야 해요. 「공간 키」에서 사이트 인증을 하거나 공간운영자 메일로 로그인해 주세요.
내 포탈
포탈 대상 고르기 같은 분류·지금 접속자 순
내 사이트에 넣는 코드
글쓰기
3D 광고
광고 API
아래 한 줄을 공간 페이지에 넣고, three.js 공간이면 장면을 만든 뒤 자리를 불러요. 광고 키는 공개해도 되는 키예요(공간 키는 넣지 마세요). 자세한 건 3D 광고 안내 · AI에게는 /ads/llms.txt를 주세요.
광고 모델의 크기는 공간에 따라 공간운영자·메타모아가 조절할 수 있어요.
광고 자리
SDK로 등록된 자리는 꺼진 채로 들어와요. 위치를 확인하고 켜 주세요.
최근 30일
대표 캐릭터 보내기
대표 캐릭터를 보내 주면 모아 야영지에서 우리 공간 포탈 옆 고블린이 그 캐릭터로 바뀌어요. 자동 검사를 통과하면 바로 서고, 새 캐릭터를 검사하는 동안에는 지금 캐릭터가 그대로 있어요.
- 3D 모델 GLB 파일, 4MB까지. 움직임(애니메이션)은 없어도 되고, 있으면 하나만 써요(이름에 idle이 있으면 그것).
- 그림 PNG 파일, 1MB까지. 서서 도는 그림으로 보여요.
- 다른 회사 로고나 남의 게임·영화·만화 캐릭터는 받지 않아요. 자세한 기준은 API 안내에 있어요.
지금 서 있는 캐릭터: 고블린
계좌번호 대신, 공간에서 결제 받기
계좌번호를 붙여 두지 않아도 흐름 결제로 공간 안에서 바로 결제를 받고 상품을 팔 수 있어요. 흐름 판매자 가입은 이 화면 아래 「메타모아 파트너사 특전」에서 시작해요.
공간 정보 고치기
고친 내용은 저장하면 바로 목록에 반영되고, 바뀐 기록이 남아요. 새 공간은 등록 요청에서 같은 칸으로 올릴 수 있어요.
업데이트 소식
업데이트 소식과 이벤트는 「등록」 화면에서 이 공간을 골라 올려요. 이 브라우저에서 확인을 마쳤으니 바로 올릴 수 있어요.
메타모아 파트너사 특전
- 판매 수수료 5% (1년간)
- 카드 수수료 실비
- 지급 신청 시 100원(부가세 별도)의 수수료가 들어가요
- 토스 앱 판매분은 앱마켓·토스 수수료를 뺀 뒤 정산
- 모두 부가세 별도
결제는 흐름에서 판매자 등록과 결제 키를 따로 받아요. 흐름 판매자 등록 때 이 공간 주소를 넣으면 인증된 판매처가 돼요.
공간운영자 메일
여기 있는 메일로 로그인(인증번호·Google)해서 이 공간을 함께 관리해요. 운영 소식도 이 메일로 보내요.
공간운영자 추가하기
메일을 넣고 「추가」를 누르면 그 메일로 확인 링크가 가요. 받은 사람이 링크를 열고 그 메일로 로그인하면 공간운영자가 돼요. 그 전에는 「대기 중」으로 보여요.
최근 바뀐 기록
메타모아 앱 받기 · App Store · Google Play
제작자 커뮤니티 — 공간운영자끼리 공간연동(포탈)·구인·협업 이야기를 나눠요.
공간운영자 확인과 수정은 이용약관과 개인정보처리방침에 따라 처리해요. 공간운영자가 아닌 것이 확인되면 연결을 끊고 고친 내용을 되돌릴 수 있어요.
메타모아 공간운영자 API · metamoa operator API
메타모아(https://metamoa.com)에 올라간 웹 메타버스 공간의 공간운영자가 쓰는 API예요. 공간 키 하나로 아래 일을 할 수 있어요.
For operators of spaces listed on metamoa. One space key does all of this:
- 업데이트 소식 올리기 · Post an update —
POST /api/update/{space_id} - 접속자 수 보내기 · Send the visitor count —
POST /api/live/{space_id} - 공간 정보 보기·고치기 · Read and edit your space —
GET/POST /api/space/{space_id} - 키 재발급 · Rotate the key —
POST /api/space/{space_id}/key
키 없이 쓰는 것 · Without a key:
- 새 공간 등록 신청 · Sign up a new space —
POST /api/submit
공간 키로 더 · Also with the space key (공간운영자 확인 뒤 · after the operator check):
- 이벤트 등록 · Space events —
POST /api/space/{space_id}/events - 야영지 캐릭터 올리기 · Camp character —
PUT /api/space/{space_id}/character
- 기본 주소 · Base URL:
https://metamoa.com - 공간 id · Space id: 공간 페이지 주소
https://metamoa.com/s/{space_id}의 끝부분 (the last part of your space page URL) - 형식 · Format: JSON, UTF-8
키 받기 · Getting a key
- metamoa.com/owner에서 소유자 인증 → 키 받기
- Verify ownership at metamoa.com/owner → get your key.
- 소유자 인증을 받으면 키를 직접 발급·재발급할 수 있어요. 받은 확인 코드를 사이트에 넣으면 돼요:
<meta name="metamoa-site-verification" content="…">, 루트의metamoa-….html파일, DNS TXTmetamoa-site-verification=…중 하나(네이버 서치어드바이저·구글 서치 콘솔과 같은 방식). 확인 뒤 「공간운영자 메일」 칸에 메일을 넣고 저장해요(아래 소유자 인증과 공간운영자 메일). 메일 확인은 보조로 그대로 돼요. · Verify ownership at metamoa.com/owner with a code placed as a meta tag, a root HTML file or a DNS TXT record (like Search Console); then issue the key yourself. Put your mail in 공간운영자 메일 after the check and save it (see below). Mail stays as a second way. - 결제는 흐름에서 키를 따로 받아요: https://console.hrum.ai/login.html?next=%2Fapikeys.html
- Payments take a separate key from 흐름 (hrum.ai); the metamoa key does not do payments: https://console.hrum.ai/login.html?next=%2Fapikeys.html
- 사업자가 아닌 개인 판매자로 등록하면 게임성이 있는 공간(게임으로 분류될 수 있는 공간)은 판매하기 승인이 안 돼요. 사업자 판매자는 자기 이름으로 판매해요.
- As an individual seller (not a registered business), a space with game elements (one that could be classed as a game) is not approved for selling. A business seller sells under its own name.
- 키를 새로 받으면 예전 키는 바로 멈춰요. · A new key stops the old one at once.
- 키는 비밀로 보관하세요. 서버에서만 쓰고 브라우저 코드에 넣지 마세요. · Keep it secret; use it from your server, never in browser code.
소유자 인증과 공간운영자 메일 · Ownership check and the operator mail
사이트 인증 코드는 메일 없이 바로 받아요. 메일은 소유 확인 뒤 https://metamoa.com/owner 맨 위 「공간운영자 메일」 칸에 넣고 「저장」을 눌러요. 확인 메일 없이 바로 저장되고, 그 메일로 다른 기기에서도 「메일로 확인」 로그인이 되며, 이 공간의 연락 메일도 그 메일로 바뀌어요(이미 있던 연락 메일도 바뀌어요). 비워 둔 채 공간 관리 화면을 떠나려 하면 지금 넣을지 물어봐요.
The site check code needs no mail. After the check, put your mail in 공간운영자 메일 at the top of https://metamoa.com/owner and press 저장: it is saved at once (no confirmation mail), signs you in on other devices (메일로 확인) and becomes this space's contact mail, replacing any earlier one. Leaving the owner page with it empty asks you first.
1. 코드 받기 · Get a code (키 없이 · no key)
POST https://metamoa.com/api/owner/site
{"id": "{space_id}"}
성공 · Success — 200
{"id": "{space_id}", "name": "산사", "code": "{code}", "meta": "<meta name=\"metamoa-site-verification\" content=\"{code}\">",
"file": {"name": "metamoa-{code}.html", "url": "https://{your host}/metamoa-{code}.html", "body": "metamoa-site-verification: {code}"},
"dns": {"host": "{your host}", "type": "TXT", "value": "metamoa-site-verification={code}"},
"secret": "{secret}", "expires_at": "2026-11-17T09:00:00.000Z"}
meta·file·dns중 하나를 사이트에 넣어요. 쓰지 않은 코드는 7일 뒤 멈춰요. · Put one ofmeta,fileordnson your site. An unused code stops after 7 days.secret은 비밀이에요. 코드를 받은 쪽만 확인할 수 있어요.code·secret을 다시 보내면 같은 코드가 와요. · Keepsecret: only its holder can finish the check. Sendingcodeandsecretagain returns the same code.
2. 확인하기 · Check
POST https://metamoa.com/api/owner/site/check
{"id": "{space_id}", "code": "{code}", "secret": "{secret}"}
성공 · Success — 200
{"ok": true, "id": "{space_id}", "method": "meta", "session": "mm_own_…", "expires_at": "2026-12-10T09:00:00.000Z"}
- 코드가 아직 안 보이면
{"ok": false, "tried": {...}}, 다른secret이나 만료된 코드는410. · Not found yet →ok: falsewith what was tried; anothersecretor an expired code →410. contact_email(선택 · optional): 넣으면 확인되는 순간 그 메일이 이 공간의 연락 메일이 되고, 응답contact에 가린 주소가 와요. 없어도 돼요. · Optional: becomes this space's contact mail when the check passes (contact, masked).
3. 공간운영자 메일 넣기 · Add the operator mail
POST https://metamoa.com/api/owner/mail
Authorization: Bearer {session}
{"id": "{space_id}", "email": "[email protected]"}
- /owner 「공간운영자 메일」 칸과 같아요. 확인 메일 없이 바로 저장돼요: 그 주소가 공간운영자 메일(다른 기기 「메일로 확인」 로그인)과 이 공간의 연락 메일이 되고, 있던 연락 메일은 바뀌어요. 응답은
{"ok": true, "saved": true, "mail": "o***@example.com", ...}. · The same as the 공간운영자 메일 box: saved at once, no mail; the address becomes an operator mail (메일로 확인 on other devices) and the space's contact mail, replacing an earlier one. - 확인이 끝나면 공간 키는 https://metamoa.com/owner 에서 받아요. 키로는
POST /api/space/{space_id}의contact_email로 연락 메일을 바꿀 수 있어요(3. 공간 정보 보기·고치기). · Then get the space key on the owner page; with the key,contact_emailofPOST /api/space/{space_id}changes the contact mail (see 3. Read and edit your space).
인증 · Authentication
모든 요청에 이 헤더를 붙여요. · Every request carries:
Authorization: Bearer {space_key}
Content-Type: application/json
1. 업데이트 소식 올리기 · Post an update
POST https://metamoa.com/api/update/{space_id}
요청 · Request
{"text": "겨울 맵이 열렸어요. 같이 걸어요!"}
성공 · Success — 201
{"ok": true, "space_id": "{space_id}", "posted_at": "2026-11-01T09:00:00.000Z", "shown_until": "2026-11-08T09:00:00.000Z"}
보이는 곳 · Where it shows
- 공간 상세 페이지에 소식 · The note on your space page
- 목록 카드에 U 표시 · A U mark on your card
- 첫 화면 「신규·업데이트」 줄 (올린 뒤 12시간, 무작위로 돌아가며) · A turn in the New & Update row on the home page for 12 hours
- 7일 동안 보여요 · Shown for 7 days
규칙 · Rules
- 하루(24시간)에 한 번 · Once per 24 hours
text200자, 3줄까지 · up to 200 characters and 3 lines- 링크는 그 공간 자기 사이트 주소만 · Links only to your own site
- 금지어가 있으면 받지 않아요. 문제가 있는 글은 운영팀이 내릴 수 있어요. · Banned words are refused; the team may hide a note.
2. 접속자 수 보내기 · Send the visitor count
POST https://metamoa.com/api/live/{space_id}
요청 · Request
{"count": 12}
성공 · Success — 204 (본문 없음 · no body)
- 지금 접속자 수를 보내면 랭킹과 첫 화면 합계에 반영돼요. · Feeds the ranking and the home total.
- 10분에 한 번 보내 주세요. 같은 공간에서 9분 안에 다시 보내면 받지 않아요. · Send every 10 minutes; a second send within 9 minutes is refused.
count는 0 이상 100000 이하 정수 · an integer from 0 to 100000- 기준 · What to count: 지금 함께 있는 사람(모든 방 합계, 최근 몇 분 안에 신호가 있는 사람, 봇·테스트 제외). 이용자 정보는 보내지 않아요. · People there right now (all rooms together, active in the last few minutes, no bots or tests). Send the number only.
3. 공간 정보 보기·고치기 · Read and edit your space
공간운영자 확인(「내 공간이에요」)을 받은 공간의 키만 쓸 수 있어요. 확인 전에 받은 키는 1·2번만 돼요.
Only for a space whose operator is verified (「내 공간이에요」). A key issued before that only does 1 and 2.
GET https://metamoa.com/api/space/{space_id}
POST https://metamoa.com/api/space/{space_id}
보기 · Read — 200
{"id": "{space_id}", "name": "…", "summary": "…", "url": "https://…", "og_image": "https://…", "langs": ["ko", "en"], "meetup": {"title": "단체 수련", "every": 10, "minutes": 5}, "listed": true, "has_key": true,
"edits": [{"field": "summary", "at": "2026-11-01T09:00:00.000Z", "actor": "owner", "reverted": false}]}
고치기 · Edit — 바꿀 칸만 보내요. · Send only what changes.
{"name": "새 이름", "summary": "한 줄 소개", "og_image": "https://example.com/og.png", "url": "https://example.com/world", "langs": ["ko", "en"],
"meetup": {"title": "소등", "times": ["21:00", "23:00"], "minutes": 30}}
성공하면 200으로 보기와 같은 본문이 와요. · Success returns 200 with the same body as Read.
contact_email(선택 · optional): 공간운영자 연락 이메일. 공간의 연락 메일로 저장하고(새 기능과 운영 시 도움이 될 이용 방법 안내만 보내요), 응답에 가린 주소(contact)가 와요. 이것만 보내도 돼요. · The operator's contact email, optional. Saved as the space's contact mail (only notices about new features and how to use metamoa); the answer carries it masked (contact). May be sent alone.notify_email(선택 · optional):true면 알림을 메일로도 보내요. 기본은false예요: 알림은 API 조회와 웹훅(PUT /api/space/{space_id}/webhook)으로 받아요. openapi.json ·truealso sends notices by mail. Defaultfalse: notices come by API reads and the webhook.name40자,summary60자 한 줄. 금지어는 받지 않아요. ·nameup to 40 characters,summaryone line up to 60; banned words are refused.og_image: 대표 이미지 주소. https 주소의 실제 이미지(PNG·JPEG·WebP·GIF·AVIF, 5MB까지)인지 확인해요. · Picture URL; it must be an https image (PNG, JPEG, WebP, GIF or AVIF, up to 5 MB).url: 지금 주소와 같은 사이트나 그 아래 주소만 · Same site as now, or a subdomain of it.langs: 지원 언어 코드 목록(배열 또는"ko,en"). 빈 목록이면 지워요. 공간 페이지에 「지원 언어」로 보여요. · Supported language codes (array or"ko,en"); empty clears; shown on your space page. 쓸 수 있는 코드 · Codes:koenjazhesfrdeptruvithidhiarittrmeetup: 정각 모임. 사람들이 같이 모이는 시각을 적으면 목록과 앱에 시작 15분 전부터 「곧 모여요」, 모임 중에는 「지금 이벤트 진행 중」으로 보여요.title(20자) +every(매 N분 정각: 10·15·20·30·60) 또는times(한국 시각"HH:MM", 8개까지) 중 하나 +minutes(한 번에 몇 분, 5~180, 없으면 10,every·다음 시각까지 간격보다 길 수 없음).null이면 지워요. · Gathering times:title(20 chars) plus eitherevery(on the clock every 10, 15, 20, 30 or 60 minutes) ortimes(Korea time"HH:MM", up to 8), andminutes(5–180, default 10, not longer thaneveryor the gap to the next time). The list and the apps show it 15 minutes before and while it runs;nullclears.- 하루 20칸까지 고칠 수 있어요. 바뀐 내용은 운영팀에 알림이 가고, 문제가 있으면 되돌릴 수 있어요. 신고도 그대로 받아요. · Up to 20 field changes a day. The team is told about every change and can revert it; reports still apply.
- 운영팀이 내린 공간은 고칠 수 없어요(
409). · A space the team hid cannot be changed (409).
4. 키 재발급 · Rotate the key
POST https://metamoa.com/api/space/{space_id}/key
성공 · Success — 200 {"space_id": "{space_id}", "token": "mm_live_…"}
- 새 키는 이 응답에만 나와요. 이 요청에 쓴 키는 바로 멈추고, 다른 키는 그대로 쓰여요. 하루 5번까지. · The new key is shown in this answer only; the key you called with stops at once, your other keys keep working. Up to 5 a day.
- 「내 공간이에요」 페이지에서는 키를 여러 개 발급하고 하나씩 멈출 수 있어요. · On the owner page you can issue several keys and stop each one.
3·4번은 공간마다 한 시간에 60번까지 불러요. · Calls to 3 and 4: up to 60 an hour per space.
5. 새 공간 등록 신청 · Sign up a new space
키 없이 보내요. 등록 신청 페이지(https://metamoa.com/submit)와 같은 대기열로 들어가 사람이 직접 열어 보고 심사해요. 이메일은 꼭 넣어요(없거나 형식이 틀리면 400). 이 이메일로 공간관리 대시보드를 이용할 수 있어요. 결과는 응답의 request_key로 GET https://metamoa.com/api/submit/{id}(Authorization: Bearer request_key)에서 보거나, webhook_url로 받아요. 승인되면 「내 공간이에요」(소유자 확인) 뒤 공간 키를 받으세요. 메일 받기(notify_email: true)를 켜면 결과 메일도 가요.
No key needed; an email is required (missing or malformed → 400). With it you use the space-management dashboard. It joins the same queue as the sign-up page (https://metamoa.com/submit) and a person opens and reviews the space. Read the result with the answer's request_key at GET https://metamoa.com/api/submit/{id}, or get it on webhook_url. Once approved, verify ownership (「내 공간이에요」) to get the space key. With mail turned on (notify_email: true) the result is mailed too.
POST https://metamoa.com/api/submit
{"name": "공간 이름", "url": "https://example.com/", "email": "[email protected]", "summary": "한 줄 소개", "webhook_url": "https://example.com/metamoa-hook",
"cat1": "휴식·힐링", "region": "kr", "og_image": "https://example.com/og.png", "live_api": "https://example.com/api/online", "langs": ["ko", "en"], "api_hookup": true, "agree": true}
성공 · Success — 201 {"ok": true, "id": "…", "request_key": "mm_req_…", "status_url": "https://metamoa.com/api/submit/…", "mail": false, "webhook": {"url": "…", "secret": "whsec_…"}} (request_key·secret은 이 응답에만 · shown only here)
- 꼭 필요한 칸 · Required:
name,url,email,region,agree: true(이용약관·개인정보 처리방침 동의 · agreeing to the terms and privacy policy) webhook_url(선택 · optional): 결과(submission.approved·submission.rejected)를 받을 https 주소(443 포트, 공개 호스트). 서명은X-Metamoa-Signature: sha256=HMAC-SHA256(secret, "<X-Metamoa-Timestamp>.<본문>"). 승인 뒤에는 공간 키로PUT /api/space/{space_id}/webhook에서 바꿔요. · An https address (port 443, public host) for the result; signed as above. After approval, change it with the space key.email(필수 · required): 공간운영자 이메일. 이 이메일로 공간관리 대시보드를 이용할 수 있어요. 없거나 형식이 틀리면400{"error": "invalid", "fields": ["email"]}. · The operator's email; with it you use the space-management dashboard. Missing or malformed →400withfields: ["email"].notify_email(선택 · optional):true면 접수·결과 메일이 가고, 승인되면email을 공간의 연락 메일로 저장해요. 기본은 메일을 보내지 않아요. ·truemails the receipt and result and keepsemailas the space's contact mail once approved. By default no mail is sent.api_hookup(선택 · optional):true면 API 연결을 요청해요. 승인되면 소유자 확인(/owner?id=…)을 마치고 키를 받아 접속자 수·업데이트·이벤트 API(1·2·6번)를 연결해요. ·trueasks for the API hookup: after approval, the owner check (/owner?id=…) issues the key.cat1(선택 · optional):GET /api/categories의 값 하나. 빼면 메타모아가 판단해서 정해요. · one ofGET /api/categories; left out, metamoa decides.mode(선택 · optional):multi(멀티, 다른 사람이 보이는 곳) 또는solo(1인용, 혼자 돌아다니는 곳).GET /api/categories의modes. 빼면 메타모아가 판단해서 정해요. ·multi(others are there) orsolo(alone); left out, metamoa decides.summary(선택 · optional): 한 줄 소개 60자. 빼면 사이트 소개(og:description)를 쓰거나 메타모아가 써요. · One line, 60 characters; left out, the site's description is used or metamoa writes one.region:kr(국내) 또는global(해외) ·kr(Korea) orglobalog_image(선택 · optional): 대표 이미지 주소. 3번과 같은 규칙으로 확인해요. · Picture URL, checked by the same rules as in 3.live_api(선택 · optional): 지금 접속자 수를 숫자로 알려 주는 주소 · An address that answers the current visitor count.langs(선택 · optional): 지원 언어 코드 목록, 3번과 같은 코드 · Supported language codes, same as in 3.url은 지금 열려야 해요(422 unreachable). 이미 있거나 심사 중인 주소는 받지 않아요(409 duplicate). ·urlmust open now; an address already listed or waiting is refused.- 한 IP에서 하루 3번, 한 메일 주소로 접수된 신청 하루 3번까지 · Up to 3 a day per IP, and 3 accepted sign-ups a day per email address.
6. 이벤트 등록 · Space events
공간에서 여는 이벤트(하루 종일 열리는 그림 대회, 매일 21:00~22:00 파티 등)를 올리면 메타모아 첫 화면 「이벤트」 줄에 보여요. 열려 있는 동안 「지금 이벤트 진행 중」, 3시간 안에 열리면 「곧 열려요」로 나와요. 공간 키(3번과 같은 규칙: 공간운영자 확인 뒤) 또는 「내 공간이에요」 로그인으로 보내요.
Events you run in your space (a drawing contest all day, a party every day 21:00–22:00) show in the 「이벤트」 strip on the metamoa home page: while open as 「지금 이벤트 진행 중」, and as 「곧 열려요」 when they open within 3 hours. Send them with the space key (same rule as 3: after the operator check) or the owner page session.
POST https://metamoa.com/api/space/{space_id}/events
GET https://metamoa.com/api/space/{space_id}/events
DELETE https://metamoa.com/api/space/{space_id}/events/{event_id}
curl -X POST https://metamoa.com/api/space/{space_id}/events \
-H "Authorization: Bearer $METAMOA_KEY" -H "Content-Type: application/json" \
-d '{"title": "그림 대회", "desc": "오늘 하루 누구나 참여", "starts_at": "2026-11-03T00:00:00+09:00", "ends_at": "2026-11-04T00:00:00+09:00"}'
curl -X POST https://metamoa.com/api/space/{space_id}/events \
-H "Authorization: Bearer $METAMOA_KEY" -H "Content-Type: application/json" \
-d '{"title": "밤 파티", "starts_at": "2026-11-03T00:00:00+09:00", "ends_at": "2026-11-10T00:00:00+09:00", "daily_from": "21:00", "daily_to": "22:00", "url": "https://example.com/party"}'
성공 · Success — 201 {"event": {"id": 1, "title": "밤 파티", "desc": null, "starts_at": "…", "ends_at": "…", "daily_from": "21:00", "daily_to": "22:00", "url": "https://example.com/party", "created_at": "…", "via": "key"}} · GET → 200 {"events": [...]} (끝나지 않은 것만 · not-ended only) · DELETE → 200 {"ok": true}
title: 30자까지, 금지어는 받지 않아요. · Up to 30 characters; banned words are refused.desc(선택 · optional): 제목 아래 한 줄 설명, 40자까지, 링크·주소는 받지 않아요. · One line under the title, up to 40 characters, no links.starts_at·ends_at: 시간대가 붙은 ISO 8601(예:+09:00). 끝이 시작보다 뒤, 기간은 31일까지, 끝이 지금보다 뒤. · ISO 8601 with an offset; the end after the start, at most 31 days, the end in the future.daily_from·daily_to(선택 · optional): 매일 열리는 한국 시각"HH:MM". 둘 다 쓰거나 둘 다 비워요. 자정까지면"24:00", 자정을 넘길 수 있어요("22:00"~"02:00"). · Daily Korea-time window; both or neither;"24:00"for midnight; may cross midnight.url(선택 · optional): 이벤트 페이지. 공간과 같은 사이트나 그 아래 주소만, 없으면 공간 주소로 열려요. · Event page on your own site (or a subdomain); without it the tile opens your space.- 공간마다 끝나지 않은 이벤트 5개까지(
409 too_many). 운영팀이 내린 공간은 새로 올릴 수 없어요(409 not_listed). 끝난 이벤트는 저절로 내려가요. · Up to 5 not-ended events per space; a hidden space cannot add events; ended events drop off by themselves.
7. 야영지 캐릭터 · Camp character
모아 야영지(https://metamoa.com/plaza)에서 내 공간의 가판대·포털 앞에 서는 캐릭터예요. 기본은 고블린이고, 파일을 올리면 자동 검사를 통과한 뒤 그 캐릭터로 바뀌어요. 공간 키(3번과 같은 규칙: 공간운영자 확인 뒤) 또는 「내 공간이에요」 로그인으로 보내요. 운영팀이 내린 공간은 올릴 수 없어요(409 not_listed).
The figure in front of your stall or portal in the 모아 camp (https://metamoa.com/plaza). It is our goblin until you send your own, which then stands there once it passes the automatic checks. Send it with the space key (same rule as 3: after the operator check) or the owner page session.
PUT https://metamoa.com/api/space/{space_id}/character
GET https://metamoa.com/api/space/{space_id}/character
DELETE https://metamoa.com/api/space/{space_id}/character
curl -X PUT https://metamoa.com/api/space/{space_id}/character \
-H "Authorization: Bearer $METAMOA_KEY" -H "Content-Type: model/gltf-binary" \
--data-binary @character.glb
curl -X PUT https://metamoa.com/api/space/{space_id}/character \
-H "Authorization: Bearer $METAMOA_KEY" -H "Content-Type: image/png" \
--data-binary @character.png
본문은 JSON이 아니라 파일 그대로 보내요. · The body is the raw file, not JSON.
받는 파일 · Files
- GLB(glTF 2.0 바이너리):
Content-Type: model/gltf-binary, 4MB까지. 삼각형 20,000개, 텍스처 4장(한 변 2048px까지, 1024px보다 크면 줄여요), 뼈대(joint) 64개, 정리한 파일 3MB까지. 바깥 파일 주소(URI)와KHR_texture_basisu는 받지 않아요. Draco·meshopt 압축은 풀어서 받고, 애니메이션은 하나만 남겨요(이름에idle이 있으면 그것). 키 1.8m로 세워요. · glTF 2.0 binary, up to 4 MB: 20,000 triangles, 4 textures (up to 2048 px a side, scaled to 1024 px), 64 joints, 3 MB after the clean-up. No external URIs, noKHR_texture_basisu; Draco and meshopt are decoded; one animation is kept (the one namedidleif any). Shown 1.8 m tall. - PNG:
Content-Type: image/png, 1MB까지, 가로·세로 1024px까지, 세로 128px 이상. WebP로 바꿔 서서 도는 그림으로 보여요. · Up to 1 MB, 1024 px a side, at least 128 px tall; re-encoded to WebP and shown as a standing, turning picture. - 공간마다 하루(24시간) 5번까지. 새로 올리면 아직 검사 중인 것은 새 것으로 바뀌어요. · Up to 5 uploads a day per space; a new upload replaces one still being checked.
검사 순서 · Checks, in order
- 받기 · Receive: 형식·크기·파일 머리(
glTFv2 / PNG 서명)를 보고 격리 저장소에 넣어요. →202. 공개 주소는 아직 없어요. · Type, size and magic bytes, then into quarantine; nothing is public yet. - 모델 검사 · Model check (따로 도는 작업, 60초 · its own process, 60 s): Khronos glTF Validator 오류 0개 → glTF-Transform으로 다시 저장(바깥 주소·
extras·모르는 확장·카메라·저작권 메타데이터를 지움) → 위 한도. 텍스처가 하나라도 안 읽히면 거절해요. 텍스처가 아예 없는 모델(색만 칠한 모델)은 운영팀이 보고 통과시켜요(review:no_texture). · Validator with no errors, re-encoded with glTF-Transform (external URIs, extras, unknown extensions, cameras and metadata dropped), then the limits. A texture that does not decode is a rejection; a model with no texture waits for the team. - 그림 검사 · Picture check: 텍스처(PNG면 그 그림)를 AI 비전 모델이 보고 성인물·폭력·다른 회사 로고나 남의 게임·영화·만화 캐릭터면 거절해요. 검사를 못 하면(일시 오류) 통과시키지 않고
checking으로 두고 10분마다 다시 해요. 한 업로드에서 검사 오류가 하루 6번 나면 운영팀 확인 대기로 넘어가요(review:vision_retry_limit). 같은 그림은 한 번 본 판정을 그대로 써요. · An AI vision model looks at the textures (or the PNG): adult content, violence, or someone else's logo or character is rejected. If the check cannot run it is never passed: it stayscheckingand is tried again every 10 minutes; after 6 failed checks for one upload in a day it waits for the team (review:vision_retry_limit). The same pictures reuse their earlier verdict. - 공개 · Publish: 통과한 정리본만 공개 저장소로 복사하고 격리본은 지워요. 야영지에 바로 서요(
GET /api/plaza의character). 새 것이 검사 중일 때는 예전 캐릭터가 그대로 서 있어요. · Only the cleaned file is copied to public storage and the quarantine copies are deleted; it stands in the camp at once. While a new one is checked, the old one stays.
응답 · Responses
PUT→202{"status": "checking", "upload_id": "chr_…"}GET→200{"status": "live", "reasons": [], "url": "/c/{space_id}-1a2b3c4d.glb", "public_url": "https://…", "updated_at": "2026-11-03T09:00:00.000Z"}—status는none·checking·live·rejected.url은 지금 서 있는 캐릭터(없으면null),public_url은 공개 저장소 주소(있을 때만). ·urlis the one standing now (nullif none);public_urlonly when public storage has an address.reasons예 · examples:tris_over:25000>20000,textures_over:5>4,validator:…,external_uri,texture_unreadable,png_short:100<128,png_over:2048x2048>1024,vision:adult,vision:violence,vision:third_party_logo,vision_pending:…(그림 검사 대기 · waiting for the picture check),review:no_texture,review:vision_retry_limit(운영팀 확인 대기 · waiting for the team)DELETE→204(고블린으로 돌아가요 · back to the goblin)
8. 포탈 · Portals
메타모아에 올라간(승인된) 공간끼리 서로의 사이트에 「포탈」을 넣어요. 포탈은 메타모아 문·모아 야영지·커스텀 세 가지이고, 문 안에 상대 공간의 메타모아 썸네일이 보여요. 한쪽이 신청하고 다른 쪽이 수락해 서로 매치돼야 양쪽에 포탈이 켜지고, 어느 쪽이든 끊으면 양쪽 다 꺼져요. 공간 키(3번과 같은 규칙: 공간운영자 확인 뒤) 또는 「내 공간이에요」 로그인으로 보내요.
Listed spaces put portals to each other on their own sites. Three kinds of door (metamoa door, camp, custom) with the other space's picture inside. One side asks, the other accepts: only a match turns the portals on at both, and either side's cut turns both off. Send with the space key (same rule as 3) or the owner page session.
GET https://metamoa.com/api/space/{space_id}/portals/suggest?c={분류}&limit=12
GET https://metamoa.com/api/space/{space_id}/portals
POST https://metamoa.com/api/space/{space_id}/portals {"to": "{other_id}", "message": "같이 이어요"}
POST https://metamoa.com/api/space/{space_id}/portals/{other_id}/accept
DELETE https://metamoa.com/api/space/{space_id}/portals/{other_id}
GET https://metamoa.com/api/portals/{space_id}?from={other_id} (키 없음 · no key, CORS *)
GET https://metamoa.com/portal/{from_id}/{to_id} (들어가기 링크 · the door link, 302)
curl https://metamoa.com/api/space/{space_id}/portals/suggest -H "Authorization: Bearer $METAMOA_KEY"
curl -X POST https://metamoa.com/api/space/{space_id}/portals \
-H "Authorization: Bearer $METAMOA_KEY" -H "Content-Type: application/json" \
-d '{"to": "{other_id}", "message": "산책 공간끼리 이어요"}'
curl -X POST https://metamoa.com/api/space/{space_id}/portals/{other_id}/accept -H "Authorization: Bearer $METAMOA_KEY"
응답 · Responses
suggest→200{"space_id": "…", "chip": "산책", "items": [{"id": "…", "name": "…", "chip": "산책", "thumb": "https://metamoa.com/img/…-480.webp", "online": 12, "operator": true, "page": "https://metamoa.com/s/…"}]}— 포탈 대상 고르기: 같은 분류 먼저, 그다음 지금 접속자 많은 순, 공간운영자 확인된 공간, 목록 순. 나와 이미 연결·신청 중인 공간은 빠져요.c로 분류를 바꾸고,limit은 1~50(기본 12). · Targets: the same category first, then people there now, then spaces with an operator, then list order.GET portals→200{"space_id": "…", "connected": [Portal], "incoming": [Request], "outgoing": [Request], "clicks": {"days": 7, "out": {"{other_id}": 5}, "in": {"{other_id}": 3}}}— 받은 신청(incoming)·보낸 신청(outgoing)과 7일 동안 포탈로 보낸·받은 클릭 수.POST portals→201{"portal": {…, "status": "pending", "asked_by": "me"}}· 상대가 먼저 신청해 둔 경우 바로200{"portal": {…, "status": "connected"}}accept→200{"portal": {…, "status": "connected"}}·DELETE→200{"ok": true, "removed": true}(보낸 신청 취소·받은 신청 거절·연결 해제 · cancel, refuse or disconnect)- Portal:
{"id", "name", "url", "thumb", "online", "return_url", "page", "since"}url: 메타모아를 거쳐 상대 공간으로 들어가는 링크(/portal/{내 id}/{상대 id}). 도착 주소 끝에?mm_portal={보낸 공간 id}가 붙어요. · goes through metamoa; the landing address gets?mm_portal={from id}.thumb: 상대 공간의 메타모아 썸네일(우리 복사본, CORS 열림 → WebGL 텍스처로 써도 돼요), 없으면 공간 대표 이미지, 없으면null. · our copy of the picture (CORS open), else the space's own image, elsenull.online: 지금 접속자 수(목록과 같은 숫자) 또는null. · people there now, as on the list.return_url: 상대 공간이 「돌아가기」로 두는 링크(/portal/{상대 id}/{내 id}). · the way back the other space puts up.
- Request:
{"id", "name", "thumb", "online", "page", "message", "created_at"}
규칙 · Rules
- 양쪽 다 목록에 오른(승인된) 공간이어야 해요. 해외·플랫폼·서비스 종료 공간은 포탈 대상이 아니고, 19+ 공간은 19+ 공간끼리만 이어요(
409 not_allowed). · Both spaces must be listed; overseas, platform and closed spaces are not portal targets; 19+ only with 19+. - 공간마다 연결 30개, 보낸 신청 대기 20개(
409 too_many), 신청 하루 20번(429).message는 200자·3줄, 링크 없이. · 30 portals, 20 open asks, 20 asks a day; message up to 200 characters, no links. - 포탈 클릭은 접속자로 세지 않아요.
/portal/…클릭은 따로 「portal」 출처로만 세요(위clicks). 들어가기 수·인기순·랭킹·지금 접속자 수·누적 방문 어디에도 더하지 않아요. 봇, 같은 사람의 1분 안 같은 포탈은 안 세요. · A portal click is never counted as a visitor: it is kept apart (theclicksabove) and never added to entries, popularity, rankings, live counts or visits. - 상대 공간의 지금 접속자 수는 그 공간이 보내는 숫자(2번 · 접속자 수 보내기) 그대로예요. · The other space's number is the one it sends itself (2).
- 새 신청이 오면 메타모아가 상대 공간의 확인된 공간운영자 메일로 알려 드려요(수락·거절 링크, 7일·한 번만). 받아 둔 공간운영자 메일이 없으면 메일 없이 공간운영자 페이지(/owner)와 제작자 커뮤니티에만 떠요. 같은 두 공간 사이 메일은 하루 1통. · A new ask is mailed to the other space's verified operator mail (one-time accept / refuse links, 7 days); without one it shows on /owner only.
포탈 세 가지 · Three kinds (https://metamoa.com/portal-kit에서 내려받기·미리보기 · download and preview)
- 메타모아 문 스타일 · metamoa door — 로고 문 모양 그대로, 리얼(
metamoa-real)·귀여운(metamoa-cute) 두 가지 - 모아 야영지 스타일 · camp — 모아 야영지에서 고블린이 여는 타원 포탈(
camp), 메타모아 색(고블린 주황#e2663a→#f2a65a) - 커스텀 · custom — 아래 주소의 값으로 색·크기·테두리·글자를 바꿔 받아요
파일 · Files ({style} = metamoa-real · metamoa-cute · camp): https://metamoa.com/portal/portal-{style}.svg, portal-{style}-frame.svg(문틀만, 안쪽 투명), portal-{style}-256.png·-512.png·-1024.png, portal-{style}.gltf(노드 portal-frame 문틀 + portal-inner 안쪽 평면, UV 0~1, 재질 portal-thumb = 썸네일 자리), portal-mask-door.svg·portal-mask-camp.svg(안쪽 모양), 전부: https://metamoa.com/portal/portal-kit.zip
커스텀 포탈 · Custom door (키 없음 · no key, CORS *, 5분 캐시)
GET https://metamoa.com/portal.svg?style=metamoa-cute&color=e2663a&size=256&label=산사로%20가기&from={space_id}&to={other_id}
GET https://metamoa.com/portal.png?… (같은 값, PNG · same values as a PNG)
| 값 · Param | 뜻 · Meaning |
|---|---|
style | metamoa-real(기본) · metamoa-cute · camp |
color · color2 | 16진수 색(# 없이, 예 e2663a). 기본은 메타모아 색 e2663a → f2a65a |
border · border_color | 테두리 굵기 0~3, 테두리 색 |
size | 가로 px 32~1024 (기본 256) |
label | 문 아래 글자, 20자 한 줄, 링크·금지어 안 됨 |
part | full(기본) · frame(안쪽 투명) · mask(안쪽 모양) |
from · to | 서로 매치된 두 공간 → 문 안에 to의 썸네일. 매치 전·목록에 없으면 기본 그림 |
틀린 값은 400 {"error": "invalid", "fields": [...]} · Bad values answer 400 with the fields.
문 너머 다른 세상 · The other world behind the door: 문 안의 썸네일은 평평하게 붙이지 않고, 보는 각도(마우스·폰 기울기·3D 카메라)에 따라 보이는 부분이 달라져요. 아래 예시가 모두 그렇게 그려요(움직임 줄이기 설정이면 멈춰요). · The picture inside moves with the viewing angle (pointer, tilt, 3D camera), still under reduced motion.
웹 페이지 · Web page (2줄 · two lines)
<div data-metamoa-portals="{space_id}" data-style="metamoa-real"></div>
<script src="https://metamoa.com/portal/portal.js" async></script>
data-style(metamoa-real · metamoa-cute · camp)과 data-color · data-color2 · data-border · data-border-color · data-size로 커스텀 값도 넣어요. 매치된 포탈만 나오고, ?mm_portal=로 들어온 사람에게는 「돌아가기」 문이 먼저 보여요. 아이폰은 「기울여 보기」 버튼으로 기울기 권한을 받아요. · Only matched portals show; a visitor who came through one gets a 「돌아가기」 door first.
직접 그리기 · To draw them yourself
const from = new URLSearchParams(location.search).get('mm_portal');
const r = await fetch(`https://metamoa.com/api/portals/{space_id}${from ? `?from=${encodeURIComponent(from)}` : ''}`).then((x) => x.json());
for (const p of [r.back, ...r.portals].filter(Boolean)) {
const a = Object.assign(document.createElement('a'), { href: p.url, className: 'door' });
a.innerHTML = '<span class="win"><span class="pic"></span></span><img src="https://metamoa.com/portal.svg?style=metamoa-real&part=frame" alt="">';
a.querySelector('.pic').style.backgroundImage = `url(${p.thumb})`;
box.append(a);
}
// .win: mask: url(https://metamoa.com/portal.svg?style=metamoa-real&part=mask) 0 0 / 100% 100%; .pic: inset -14% (문보다 크게)
addEventListener('pointermove', (e) => {
const x = e.clientX / innerWidth - 0.5, y = e.clientY / innerHeight - 0.5;
for (const pic of document.querySelectorAll('.door .pic')) pic.style.transform = `translate(${-x * 18}px, ${-y * 18}px)`;
});
three.js (각도 효과 셰이더 · the parallax shader)
const { scene: door } = await new GLTFLoader().loadAsync('https://metamoa.com/portal/portal-metamoa-real.gltf');
const map = await new THREE.TextureLoader().setCrossOrigin('anonymous').loadAsync(p.thumb);
map.colorSpace = THREE.SRGBColorSpace;
door.getObjectByName('portal-inner').material = new THREE.ShaderMaterial({
uniforms: { map: { value: map }, depth: { value: 0.35 } }, side: THREE.DoubleSide,
vertexShader: `varying vec2 vUv; varying vec3 vDir;
void main() { vUv = uv; vDir = position - (inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,
fragmentShader: `uniform sampler2D map; uniform float depth; varying vec2 vUv; varying vec3 vDir;
void main() { vec3 d = normalize(vDir); vec2 shift = d.xy / max(abs(d.z), 0.2) * depth;
gl_FragColor = texture2D(map, clamp((vUv - 0.5) * 0.8 + 0.5 + shift * 0.5, 0.0, 1.0));
#include <colorspace_fragment>
}`,
});
door.userData.href = p.url; // 클릭(raycast)하면 location.href = door.userData.href
scene.add(door);
공간운영자끼리 포탈을 찾고 신청·수락하는 곳: 제작자 커뮤니티의 「공간연동」 · Find and agree on portals with other operators in the 공간연동 board of the Creator Community.
9. 결제 · Payments
공간 안에서 상품을 팔고 결제를 받아요. 결제와 정산은 흐름(hrum.ai)이 하고, 메타모아는 흐름 판매자 가입을 이어 주고 공간에 넣을 결제 버튼을 드려요. 공간운영자 사이트에 결제 스크립트를 넣지 않아도 돼요. 판매하기는 공간 관리(「내 공간이에요」 로그인)에서만 하고, 공간 키로는 할 수 없어요.
Sell products inside your space. 흐름 (hrum.ai) takes the payments and pays you out; metamoa links you to 흐름's seller sign-up and gives you a payment button for your space. No payment script on your site. Selling is set up on the owner page (공간 관리) only, not with the space key.
순서 · Steps
- 흐름 판매자 가입 · Sign up as a 흐름 seller — 공간 관리 → 「메타모아 파트너사 특전」 칸의 판매하기를 누르면 흐름 판매자 가입 화면으로 가고, 끝나면 공간 관리로 돌아와요. 판매자는 개인·개인사업자·법인 중 하나로 가입해요. 목록에 오른(승인된) 공간만 시작할 수 있어요. · On the owner page press 판매하기 in the 메타모아 파트너사 특전 box: it opens 흐름's seller sign-up and comes back to the owner page. Sign up as an individual, a sole proprietor or a company. Only listed spaces can start.
- 판매 심사 · Review — 흐름이 판매자를 승인하고, 메타모아가 공간을 검수해요. 메타모아 검수는 판매자 유형과 게임성(점수·레벨·보상·경쟁·아이템·확률형·승패)을 봐요. 사업자가 아닌 개인 판매자는 게임성이 있는 공간이면 승인되지 않아요. 사업자 판매자는 게임성으로 막지 않아요. 공간 관리에 상태가 보여요: 신청 중 · 심사 중 · 보완 필요 · 반려 · 승인, 그다음 검수 중 · 검수 반려(이유와 함께). · 흐름 approves the seller and metamoa reviews the space for the seller type and game elements (score, levels, rewards, competition, items, random rewards, win or lose). An individual seller (not a business) is not approved for a space with game elements; business sellers are not blocked for it. The owner page shows the state, with the reason when the review is refused.
- 상품 만들기 · Make products — 흐름이 판매를 허락하면 판매하기 칸에 상품 칸이 열려요. 상품 이름을 쓰고 가격을 목록에서 골라 「상품 추가」를 눌러요. 메타모아 검수 중에도 상품을 미리 만들 수 있고, 결제는 검수가 끝나면 열려요. 검수 반려 뒤에는 상품을 추가할 수 없어요. 「삭제」하면 그 상품의 결제 버튼도 멈춰요. · Once 흐름 allows selling, the product form opens: a name and a price from the list, then 상품 추가. Products can be added during metamoa's review; payments open when it passes. After a refused review no product can be added. Deleting a product stops its button.
- 공간에 결제 버튼 넣기 · Put the payment button in your space — 상품마다 공간 관리에 결제 버튼 코드가 나와요. 「복사」해서 공간 페이지 어디에든 넣으면 돼요. 그냥 링크라 모양은 마음대로 꾸며도 돼요. · Each product shows its button code on the owner page; copy it anywhere into your page. It is a plain link, so style it as you like.
누르면 메타모아를 거쳐 흐름 결제 페이지로 가고, 결제가 끝나면 메타모아의 「결제 완료」(또는 「결제 실패」) 페이지로 돌아와요. 아직 검수 중인 상품은 「검수 중이에요」, 판매가 열리지 않은 상품(검수 반려, 목록에서 내려간 공간, 지운 상품)은 「결제할 수 없어요」가 떠요. 가격은 흐름에 등록된 값으로만 결제돼요. · A click goes through metamoa to 흐름's checkout; afterwards the buyer lands on metamoa's 결제 완료 (or 결제 실패) page. A product that is not on sale shows 결제할 수 없어요. Only the price registered at 흐름 is charged.<a href="https://metamoa.com/buy/{sku_id}">구매하기</a> - 결제 완료 알림 받기 · Get told about payments — 구매자는 공간운영자 사이트가 아니라 메타모아 결제 완료 페이지로 돌아와요. 서버에서 결제를 알려면 흐름 판매자 웹훅을 써요. 흐름에서 받은 결제 키(키 받기, 권한
webhooks)로 내 서버 주소를 등록하면 결제 완료(order.paid)·환불(order.refunded)·지급 예정(payout.scheduled)·지급 완료(payout.completed)가 서명과 함께 와요. 웹훅에는 금액과 주문번호만 있고 구매자 정보는 없어요. 어떤 상품인지는 주문번호로 흐름 주문 조회(권한orders.read)를 불러skuId로 맞추고,status가paid일 때만 상품을 주세요. 서명 확인과 다시 보내기는 흐름 판매자 웹훅 문서에 있어요. 메타모아 웹훅(webhook_url)에는 결제가 오지 않아요. · The buyer comes back to metamoa, not to your site, so use 흐름's seller webhook: register your server address with your 흐름 payment key (Getting a key, scopewebhooks) and get signedorder.paid,order.refunded,payout.scheduledandpayout.completed. A webhook carries amounts and the order id only, no buyer details; look the order up (scopeorders.read) to match theskuId, and hand over the product only whenstatusispaid. Signature checks and redelivery: 흐름 seller webhooks. The metamoa webhook (webhook_url) carries no payments.PUT https://api.hrum.ai/api/v1/partners/v1/webhooks/endpoint {"url": "https://example.com/hrum/webhook"} GET https://api.hrum.ai/api/v1/partners/v1/storefront/orders/{orderId} X-Partner-Api-Key: $HRUM_PARTNER_KEY - 정산 · Payouts — 정산과 지급은 흐름이 해요. 흐름 콘솔 「정산」에서 정산 계좌를 등록하고 판매·지급을 봐요. 공간 관리 판매하기 칸의 「판매 현황 보기」가 그 화면(https://console.hrum.ai/settlement.html)이에요. 서버에서 보려면 흐름 정산 데이터 API(권한
settlement.read)를 써요. 수수료는 공간 관리 판매하기 칸에 나와요. · 흐름 settles and pays you out. Register your payout account and see sales and payouts in 흐름 console 정산; 판매 현황 보기 on the owner page opens it. From a server use 흐름's settlement API (scopesettlement.read). Fees are shown in the 판매하기 box.GET https://api.hrum.ai/api/v1/partners/v1/settlement/summary?from={YYYY-MM-DD}&to={YYYY-MM-DD} X-Partner-Api-Key: $HRUM_PARTNER_KEY
오류 · Errors
| 코드 · Status | 본문 · Body | 뜻 · Meaning |
|---|---|---|
| 400 | {"error":"invalid","fields":["text"],"reason":"..."} | 글이 비었거나 길거나, 금지어·다른 사이트 링크 · empty, too long, banned words or a foreign link |
| 400 | {"error":"invalid","fields":["count"]} | count가 정수가 아니거나 범위 밖 · count not an integer in range |
| 400 | {"error":"invalid","fields":["summary"],"reasons":{"url":"other_site"}} | 3번: 잘못된 칸. reasons 값은 invalid·other_site·url_taken·not_an_image · bad field in 3 |
| 401 | {"error":"unauthorized"} | 키가 없거나 틀리거나 다른 공간 키 · missing, wrong or another space's key |
| 403 | {"error":"owner_required","claim":"https://metamoa.com/owner?id=…"} | 3·4·6·7번: 공간운영자 확인 전 · operator not verified yet |
| 404 | {"error":"not_found"} | 목록에 없는 공간 · space not listed |
| 409 | {"error":"not_listed"} | 3·4·6·7번: 운영팀이 내린 공간 · the team hid this space |
| 400 | {"error":"invalid","fields":["og_image"]} | 5번: 빈 칸·잘못된 형식(https가 아닌 이미지 주소 등). fields에 문제 칸 · missing or malformed fields (e.g. a non-https picture URL) |
| 409 | {"error":"duplicate"} | 5번: 이미 있거나 심사 중인 주소 · address already listed or waiting |
| 422 | {"error":"unreachable"} · {"error":"not_an_image","fields":["og_image"]} | 5번: 주소가 안 열리거나 이미지가 아님 · URL does not open, or the picture is not an image |
| 413 | {"error":"too large"} | 본문이 너무 큼 · body too large |
| 429 | {"error":"too_soon","retry_after":3600} | 너무 빨리 다시 보냄. retry_after초 뒤에 다시 · too soon; retry after retry_after seconds (also in the Retry-After header) |
| 429 | {"error":"too_many"} | 너무 많이 보냄(IP·공간·메일 주소별 한도) · too many requests (per IP, space or address) |
| 400 | {"error":"invalid","fields":["file"],"reason":"not_glb_or_png"} | 7번: GLB·PNG가 아니거나 Content-Type이 다름 · not a GLB or PNG, or the wrong type |
| 413 | {"error":"too large","max_bytes":4194304} | 7번: 파일이 한도보다 큼(GLB 4MB, PNG 1MB) · file over the limit |
| 429 | {"error":"too_many","retry_after":3600} | 7번: 공간마다 하루 5번 넘음 · over 5 uploads a day |
예시 · Example (curl)
curl -X POST https://metamoa.com/api/update/{space_id} \
-H "Authorization: Bearer $METAMOA_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "새 맵이 열렸어요!"}'
curl -X POST https://metamoa.com/api/live/{space_id} \
-H "Authorization: Bearer $METAMOA_KEY" \
-H "Content-Type: application/json" \
-d '{"count": 12}'
curl -X POST https://metamoa.com/api/space/{space_id} \
-H "Authorization: Bearer $METAMOA_KEY" \
-H "Content-Type: application/json" \
-d '{"summary": "겨울 맵이 열린 산책 공간"}'
10. 3D 광고 · 3D ads
자세한 안내: https://metamoa.com/ads/docs · AI용: https://metamoa.com/ads/llms.txt
광고 등록: https://metamoa.com/ads/manage · 공간운영자 안내: https://metamoa.com/ads/partners
한눈에 보기
메타모아 3D 광고는 광고주의 3D 모델·광고용 건물·광고 캐릭터를 웹 메타버스 공간 안 광고 자리에 띄우고, 사람이 실제로 본 것과 한 일만 세어 광고비를 받고 그 몫을 공간운영자에게 나누는 광고예요. 광고업·광고대행업으로 대기업부터 중소기업까지 받아요.
- 공간운영자: 공간 관리 수익 창출(/owner/revenue)의 「광고 게재 등록하기」(약관 확인·정산 정보·광고 자리) → 스크립트 한 줄과 광고 자리를 넣으면 끝.
- 광고주: /ads/manage에서 메타모아 계정으로 로그인 → 3D 모델(glb)을 올리거나, 광고용 건물을 골라 이미지·텍스처를 넣거나, 기본 광고 캐릭터에 소품과 광고를 고르기 → 노출 공간을 직접 고르거나 「추천으로」 → 과금 방식·예산·기간 → 검수 뒤 집행.
- 모델이 없으면 「모델 제작 요청」을 고르면 우리가 만들어 드려요. 모델을 직접 주셔도 받아요. 기본 광고 캐릭터는 금액 상관없이 무료예요.
- 과금은 넷: CPM(viewable 노출), CPA(모델 클릭·브랜드 공간 입장), 노출 시간(보인 누적 시간), 공간 고정 계약(기간제, 자리 독점). 금액은 모두 (공급가, 부가세 별도).
- 광고 모델의 크기는 공간에 따라 공간운영자·메타모아가 조절할 수 있어요.
상품 4종과 과금
금액은 모두 공급가, 부가세 별도예요.
| 상품 | billing | 세는 것 | 단가 |
|---|---|---|---|
| CPM (viewable 노출) | cpm | viewable 노출 1회 = 모델이 화면 안에 일정 몫 이상, 정해진 시간 넘게 이어서 보임(탭이 보이는 상태) | viewable 1,000회당 8,000원 |
| CPA · 모델 클릭 | cpa | 광고 모델을 탭·클릭. 클릭한 뒤 브랜드 공간 입장까지 가면 입장 1건만 청구 | 클릭 1회당 700원 |
| CPA · 브랜드 공간 입장 | cpa | 광고를 눌러 브랜드 공간(brand_url)으로 들어감. 서버 리다이렉트(/ads/go)로 세고, 같은 사람·캠페인은 하루 한 번 | 입장 1회당 1,000원 |
| 노출 시간 | time | viewable 노출 뒤 광고가 보인 누적 시간(입력 활동이 있을 때만, 한 사람·모델 하루 상한까지) | viewable 1,000초당 1,000원 |
| 공간 고정 계약 | fixed | 기간제(최대 한 달). 계약한 광고 자리를 그 기간 동안 혼자 씀(자리 단위). 노출 수는 보장하지 않음 | 아래 등급표 |
공간 고정 계약 등급
등급은 그 자리가 있는 공간의 최근 14일 평균 하루 공간 클릭으로 정해요. 가장 낮은 등급에 못 미치는 공간의 자리는 공간 고정 계약으로 팔지 않고 성과형으로만 받아요. 계약 기간은 주간 7일 · 월간 30일 중 하나예요.
| 등급 | 하루 공간 클릭 | 주간(자리 1개) | 월간(자리 1개) |
|---|---|---|---|
| S | 1,000 이상 | 50,000원 | 180,000원 |
| A | 300~999 | 15,000원 | 50,000원 |
| B | 100~299 | 5,000원 | 18,000원 |
- 최소 집행: 매체비 200,000원 (공급가, 부가세 별도), 모델 제작비 제외. 성과형은 예산, 공간 고정 계약은 계약 금액 합으로 봐요.
- 성과형(CPM·CPA·노출 시간)은 실제로 센 만큼만 청구하고, 쓴 금액이 예산(budget_won)에 닿으면 그 캠페인은 더 나가지 않아요.
- CPM 계약은 고른 자리의 예상 viewable 노출(하루 공간 클릭 × 0.7 × 일수)의 70%까지만 받아요(409 over_inventory).
- 공간 고정 계약이 잡힌 자리에는 그 기간 동안 그 캠페인 하나만 나가고, 같은 자리·겹치는 기간의 고정 계약은 받지 않아요(409 slot_taken). 자리는 검수 승인 때 잡혀요: 검수 대기 중인 신청은 자리를 묶지 않고, 먼저 승인된 계약이 이겨요. 모델이 안 뜬 날이 있거나 트래픽이 30% 넘게 떨어지면 일할 환불이나 연장으로 맞춰요.
- 청구와 등급은 메타모아 실측 리포트(viewable 노출·클릭·입장·보인 시간, 공간 클릭) 기준이에요.
집계 공식
날짜는 한국 시간 하루 단위(ad_daily). 금액은 원 단위 아래를 버린 공급가예요. 규칙에 걸린 이벤트는 세지 않고 이유별로 따로 세요(ad_excluded).
- viewable 노출 imps = 규칙을 모두 통과한
imp이벤트 수 - 클릭 clicks = 통과한
click수, 입장 enters = 통과한 입장(/ads/go) 수, converted = 클릭한 뒤 같은 토큰으로 입장까지 간 수 - 보인 시간 view_ms = 통과한
time이벤트 ms 합(노출이 센 토큰만, 이벤트마다 15,000ms까지, 한 사람·모델 하루 상한까지) - CPM 금액 = floor(imps × cpm_won ÷ 1,000)
- CPA 금액 = (clicks − converted) × click_won + enters × enter_won
- 노출 시간 금액 = floor(view_ms ÷ 1,000 × time_1000s_won ÷ 1,000)
- 공간 고정 계약 금액 = 계약한 자리마다 등급 단가의 합. 기간 일부만 집계할 때는 자리마다 floor(단가 × 그 기간 안의 계약 일수 ÷ 계약 일수)
- 공간운영자 몫 = floor(공간별 금액 × 그 공간의 공간운영자 등급 비율(%) ÷ 100). 금액은 무효 트래픽을 뺀 매체 공급가이고, 모델·공간 제작비는 나누지 않아요. 등급은 아래 「공간운영자 수익 배분 등급」.
공간운영자 수익 배분 등급
광고 수익(공간별 광고 매체 금액, 무효 트래픽을 뺀 공급가)을 공간운영자와 메타모아가 등급의 비율로 나눠요. 2026년 11월 11일 시행. 우선 등록 안내: https://metamoa.com/ads/partners
| 등급 | 대상 | 공간운영자 : 메타모아 |
|---|---|---|
| 플래티넘 | 선착순 1~10번째 공간운영자 | 9:1 |
| 골드 | 11~100번째 공간운영자(90명) | 8:2 |
| 실버 | 그 뒤 공간운영자 | 7:3 |
| 브론즈 | 2027년 1월 1일부터 새로 계약하는 공간운영자 | 6:4 |
추후 광고 성과에 따라 비율은 조절 될 수 있습니다.
- 공간운영자 = 메타모아에 공간을 등록하고 공간 관리에 로그인한 소유 계정 단위예요. 순번은 공간 관리에서 처음 「광고 게재 동의」를 켠 시각 순서로, 서버가 한 번에 하나씩 매겨요.
- 한 번 정해진 등급은 그대로 유지되고, 같은 공간운영자의 다른 공간도 같은 등급이에요.
- 2027년 1월 1일 0시(한국 시간)부터 새로 계약하는 공간운영자는 순번과 상관없이 브론즈예요.
- 내 등급과 비율: 공간 관리 「3D 광고」 칸, GET /api/space/{id}/ads의 partner. 남은 자리: GET /api/ads/partners의 seats.
약관 확인과 광고 게재 동의
이용약관에 광고 게재 조항(제4조의4)이 들어갔어요. 광고를 받으려면 공간운영자가 공간 관리 수익 창출의 「광고 게재 등록하기」에서 두 가지를 한 번에 해요.
- 약관 확인: 공간 관리(/owner/revenue?id=<공간 id>) 「광고 게재 등록하기」 1단계의 약관 확인 체크. 공간 관리 화면에서만 돼요(공간 키로는 403 owner_page_only).
- 광고 게재 동의: 약관 확인과 함께 켜지고, 그 뒤 같은 화면에서 공간별로 켜고 꺼요. 켜려면 정산 정보의 유형과 연락처가 있어야 해요(없으면 409 payout_info_required). 켜야 그 공간의 광고 API(스크립트 한 줄·광고 키)가 보이고, 끄면 바로 광고가 나가지 않아요(serve 403 ads_off).
동의 기록: 공간, 누가(그 공간 관리에 로그인한 공간운영자 메일의 해시), 언제, 어느 약관 판(terms.html의 data-ads-version), 켬·끔을 지우지 않고 쌓아요(ad_consents).
약관 확인을 하지 않았거나 광고 게재 동의를 끈 공간, 목록에 오르지 않은 공간에는 광고가 나가지 않아요.
광고 자리(슬롯)
공간운영자가 자기 메타버스 안에 광고가 설 자리를 직접 정해요. 광고주는 자리 단위로 고르거나 추천을 받고, 공간 고정 계약도 자리 단위로 묶어요. 자리 목록은 공간 관리 화면에서 보고 켜고 끌 수 있어요.
| 칸 | 형식 | 뜻 |
|---|---|---|
| name | 1~40자 | 공간 안에서 겹치지 않는 이름 |
| kind | floor · wall · air · character | 바닥 모델 · 벽 간판 · 공중 배너 · 서 있거나 걸어 다니는 광고 캐릭터 |
| position | [x, y, z] 미터 | 자리 박스 바닥 가운데의 위치(공간 좌표) |
| rotation | [x, y, z] 도(-360~360) | 자리의 회전. 없으면 [0, 0, 0] |
| size | [가로, 높이, 깊이] 미터(0.1~200) | 광고가 들어갈 박스. 광고는 이 박스에 맞게 키우거나 줄여요 |
| scale | [최소, 최대] 배(0.05~20) | 공간별 크기 배율: 박스에 맞출 때 광고 원래 크기의 이 배율 안에서만 키우고 줄여요. 없으면 [0.25, 4] |
| roam | 0~50 미터 | character 자리: 캐릭터가 걸어 다니는 반지름(0이면 서 있음) |
| industries | 업종 id 목록 | 이 자리가 받는 업종. 비우면 모든 업종 |
| enabled | true · false | 끄면 이 자리에는 광고가 나가지 않아요 |
광고 모델의 크기는 공간에 따라 공간운영자·메타모아가 조절할 수 있어요.
캐릭터 광고는 character 자리에만, 모델·건물 광고는 floor·wall·air 자리에만 나가요.
SDK(공개 광고 키)로는 새 자리만 만들 수 있고, 그렇게 만든 자리는 꺼진 채로 들어가 공간운영자가 공간 관리에서 확인하고 켜야 광고가 나가요. 이미 있는 자리(같은 이름)는 SDK가 바꾸지 못하고 그대로 돌려줘요. 꺼진 SDK 자리는 공간마다 5개까지이고 공간운영자 자리 30개와 따로 세요. 한 IP 묶음은 하루 20개까지 만들 수 있고, 공간운영자가 7일 안에 한 번도 켜지 않은 SDK 자리는 지워져요(한 번 켠 자리, 캠페인이 쓰는 자리는 지우지 않아요). 켜면 공간운영자 자리 30개 안으로 들어가요. 자리의 박스·종류·업종을 바꾸고 켜고 끄는 건 공간 관리 로그인이나 공간 키로만 돼요.
업종 id: game(게임) · fashion(패션·뷰티) · food(식음료) · tech(IT·전자) · finance(금융) · edu(교육) · travel(여행·레저) · culture(문화·엔터) · auto(자동차) · public(공공·비영리) · etc(기타)
공간마다 자리 30개까지. 자리 API는 공간 키(mm_live_)나 공간 관리 로그인으로 써요:
GET https://metamoa.com/api/space/{space_id}/ads/slots
POST https://metamoa.com/api/space/{space_id}/ads/slots {"name":"plaza-front","kind":"floor","position":[0,0,-8],"rotation":[0,90,0],"size":[4,4,4],"scale":[0.5,2],"industries":["game","food"]}
POST https://metamoa.com/api/space/{space_id}/ads/slots {"name":"gate-greeter","kind":"character","position":[3,0,-4],"size":[1.2,2.2,1.2],"roam":3}
PATCH https://metamoa.com/api/space/{space_id}/ads/slots/{slot_id} {"enabled": false, "scale": [0.5, 1.5]}
DELETE https://metamoa.com/api/space/{space_id}/ads/slots/{slot_id}
공간운영자: 붙이는 순서
- 공간 관리 수익 창출(https://metamoa.com/owner/revenue?id=<공간 id>)의 「광고 게재 등록하기」에서 약관 확인에 체크하고 광고를 시작해요.
- 같은 화면 「광고 API」 칸의 스크립트 한 줄을 공간 페이지 <head>나 <body> 끝에 넣어요. 광고 키(mm_adpub_)는 공개해도 되는 키예요(공간 키 mm_live_는 넣지 마세요).
- three.js 공간이면 장면이 만들어진 뒤 attach와 slot을 불러요. 자리는 처음 부를 때 꺼진 채로 등록돼요. 공간 관리 「광고 자리」에서 켜면 광고가 나가요(자리 API로 처음부터 켠 채 만들어도 돼요).
- 광고가 보이면 끝이에요. viewable 노출·클릭·보인 시간은 SDK가 알아서 보내고, 브랜드 공간 입장은 광고를 누르면 SDK가 /ads/go로 열어 서버가 세요.
- 공간 관리의 「광고 자리」에서 자리를 켜고 끄고, 30일 집계(규칙 적용 전·후)와 배분 몫을 봐요(GET /api/space/{space_id}/ads/stats).
<script src="https://metamoa.com/ads/sdk.js" data-space="{space_id}" data-key="{mm_adpub_…}" async></script>
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// after your scene, camera and renderer exist:
(window.metamoaAdsReady = window.metamoaAdsReady || []).push((ads) => {
ads.attach({ THREE, GLTFLoader, scene, camera, renderer });
ads.slot({ name: 'plaza-front', kind: 'floor', position: [0, 0, -8], rotation: [0, 0, 0], size: [4, 4, 4] });
ads.slot({ name: 'north-wall', kind: 'wall', position: [0, 3, -15], size: [6, 3.4, 0.4] });
ads.slot({ name: 'gate-greeter', kind: 'character', position: [3, 0, -4], size: [1.2, 2.2, 1.2], roam: 3 });
});
three.js가 아닌 엔진(Babylon.js, PlayCanvas, Unity WebGL 등)은 serve로 광고를 받아 직접 그리고 이벤트를 보내요: ads.serve(name) → { ad }, ads.report(name, event). 입장은 ad.enter_url을 새 창이나 같은 창으로 열면 돼요. 규격은 아래 「이벤트 규격」.
SDK가 세는 방법
- viewable 노출: 모델의 정해진 몫 이상이 카메라 화면 안에 있고, 화면에 비친 크기가 정해진 넓이 이상인 채로, 탭이 보이는 동안 정해진 시간 넘게 이어지면 노출 1회를 보내요(serve 토큰 하나에 한 번).
- 보인 시간: 노출된 뒤 광고가 위 조건으로 보이는 동안 5초마다 쌓아 보내고, 그 사이 카메라·포인터 입력 수를 같이 보내요(탭이 숨으면 멈춤).
- serve 응답에 vid가 오면 그 브라우저에 두고, 다음 serve부터 같이 보내요(GET /ads/v1/serve?…&vid=). vid는 서버가 그 망에 서명해 준 값만 읽혀요. 서버가 vid와 접속 IP 묶음을 합쳐 그 사람을 토큰 안에 서명해 넣어요. 노출은 serve 뒤 2초쯤 지나야 세요.
- 클릭: 광고를 탭·클릭하면
click을 보내고, 광고에 브랜드 공간이 있으면 enter_url(/ads/go)을 새 창으로 열어요. 서버가 입장을 세고 브랜드 공간으로 보내요. - 브라우저 id(vid)는 서버가 주고 SDK가 localStorage에 둬요. 반복 제한에만 써요. 이름·연락처 같은 개인 정보는 보내지 않아요.
- 광고는 30분마다 새로 받아요(새 토큰). 캐릭터 자리는 roam 반지름 안을 천천히 걸어요.
봇·부정 검증 규칙
이벤트는 서버에서 아래 규칙을 차례로 거쳐요. 하나라도 걸리면 집계에서 빼고 그 규칙 이름으로 따로 세요. 규칙은 data/ads-config.json의 bot_rules에서 하나씩 켜고 꺼요.
| 규칙 | 하는 일 | 지금 |
|---|---|---|
봇 UA (ua_bot) | 검색 로봇·미리보기·스크립트(curl, python, headless 등)와 User-Agent가 없는 요청은 뺀다. | 켬 |
자동화 브라우저 (headless) | navigator.webdriver가 켜진 브라우저, HeadlessChrome·PhantomJS·Electron·jsdom, Accept-Language가 없는 요청은 뺀다. 측정 정의이고 스크립트 방어로 세지 않는다. | 켬 |
탭 가시성·포커스 (visibility_focus) | 노출은 탭이 보이고(document.visibilityState = visible) 창에 포커스가 있을 때만, 보인 시간은 탭이 보일 때만 센다. 측정 정의다. | 켬 |
viewable 판정 (min_view) | 모델의 정해진 몫 이상이 화면 안에, 정해진 넓이 이상으로, 정해진 시간 넘게 이어져야 노출 1회. 측정 정의다. | 켬 |
같은 사람·인터넷 주소 반복 제한 (repeat_limit) | 같은 사람·같은 모델의 노출과 클릭은 정해진 시간에 한 번, 입장은 하루에 한 번. 한 인터넷 주소 묶음의 시간당 상한, 공간마다 하루 새 시청자 수, 캠페인·공간마다 하루 노출·클릭·입장 수를 넘는 것은 뺀다. | 켬 |
서버 서명 토큰 (signed_token) | 이벤트는 serve가 준 서명 토큰(HMAC)을 들고 와야 한다. 보는 사람과 serve 때의 인터넷 주소 묶음이 토큰 안에 서명돼, 다른 주소에서 온 노출은 빼고, serve 뒤 정해진 시간이 지나기 전의 노출도 뺀다. 토큰은 유효 시간 안에 노출·클릭·입장 각 한 번만 센다. 캠페인 예산은 셀 때 다시 보고, 넘는 이벤트는 세지 않는다(budget). | 켬 |
노출 시간 측정 (view_time) | 보인 시간은 그 사이에 카메라·포인터 입력이 있을 때만, 토큰마다·한 사람과 모델마다 하루·한 인터넷 주소와 모델마다 하루 상한까지만 센다. | 켬 |
공간운영자 본인 트래픽 (owner_traffic) | 그 공간의 공간 관리 화면을 연 인터넷 주소에서 온 이벤트는 정해진 날 동안 뺀다. | 켬 |
사람 입력 흔적 (human_input) | 클릭은 브라우저가 사람 입력이라고 표시한(isTrusted) 것만 센다. 노출은 입력 수를 보지 않는다(가만히 보는 사람, 키보드·화면 낭독기 사용자). | 켬 |
이상치 감시와 보류 (anomaly) | 한 사람이 하루에 정해진 수를 넘게 본 노출은 뺀다. CPA 클릭·입장은 공간마다 하루 자동 인정 수까지 세고, 넘는 것은 보류한다. 클릭·입장 비율(그 공간을 뺀 네트워크와 비교), 공간 클릭 대비 노출, 인터넷 주소당 새 시청자, 데이터센터 몫, 같은 주소 반복, 클릭 대비 입장이 기준을 넘으면 그 공간의 그 달 몫을 보류하고 관리자가 판정한다. | 켬 |
데이터센터 망 (asn) | Cloudflare가 붙인 x-mm-asn(AS 번호)이 클라우드·호스팅 망이면 그 이벤트는 빼고, 그 몫이 크면 공간을 보류한다. Cloudflare Transform Rule이 같이 붙이는 비밀 헤더(origin-auth)가 서버 env 값과 맞을 때만 믿고, 없으면 그 이벤트의 망은 「모름」으로 두고 데이터센터 몫은 망을 아는 이벤트로만 잰다. | 켬 |
입장 사람 확인 (turnstile) | 브랜드 공간 입장(/ads/go)은 메타모아 도메인에서 Cloudflare Turnstile을 통과해야 센다. 실패해도 사용자는 브랜드 공간으로 보내고 입장만 세지 않는다. 키가 없을 때 들어온 입장은 보류한다. | 켬 |
- 클릭과 입장은 그 토큰의 노출이 먼저 세어진 경우에만 세요(아니면 no_impression).
- 서버가 기억하는 표가 가득 차면 아직 기간이 남은 기록을 지우지 않고, 새 이벤트를 세지 않아요(capacity). 막는 쪽으로 닫혀요.
이벤트 규격
SDK 요청은 그 공간 사이트에서만 받아요. 공간운영자 자기 도메인이면 같은 등록 도메인, github.io·vercel.app 같은 공용 호스트면 호스트 이름이 정확히 같아야 하고, 경로 아래에 있는 공간(github.io/<저장소>, claude.ai)은 Referer 경로까지 그 아래여야 해요(SDK는 전체 Referer를 보내요). 모두 CORS 열림, JSON. sendBeacon(text/plain)도 받아요.
광고 받기 · serve
GET https://metamoa.com/ads/v1/serve?space={space_id}&key={mm_adpub_…}&slot={자리 이름 또는 id}&vid={vid}
→ 200 {
"space_id": "onsen-village",
"vid": "<새 브라우저 id: 이 브라우저에 id가 없을 때만 와요>",
"slot": {
"id": "sl_k2m9x0a1b2c3d4e5",
"name": "plaza-front",
"kind": "floor",
"position": [
0,
0,
-8
],
"rotation": [
0,
0,
0
],
"size": [
4,
4,
4
],
"scale": [
0.25,
4
],
"roam": 0
},
"ad": {
"campaign_id": "cp_q8w7e6r5t4y3u2i1",
"brand": "반짝 우유",
"token": "<serve 토큰>",
"enter_url": "https://metamoa.com/ads/go?t=<serve 토큰>",
"kind": "building",
"building_id": "billboard-low",
"model_url": "https://metamoa.com/ads/models/billboard-low/billboard-low.image.glb?v=1",
"size_m": [
4,
2.24,
0.575
],
"mode": "image",
"faces": [
{
"id": "main",
"mesh": "ad_image",
"image_url": "https://metamoa.com/ads/a/as_a1b2c3d4e5f6g7h8.webp"
}
]
}
}
ad가 null이면 지금 그 자리에 나갈 광고가 없어요. kind가 model이면 model_url의 glb를, building이면 건물 glb를 놓은 뒤 mode가 image면 faces의 mesh 이름에 image_url 이미지를, texture면 texture.meshes 전체에 image_url을 repeat만큼 반복해 입혀요. character면 캐릭터 glb를 세우고 prop.attach 자리에 소품을 붙여 그 판에 image_url 이미지나 text 글자를 넣어요. 모두 slot.size 박스에 맞추되 원래 크기의 slot.scale 배율 안에서만 키우고 줄여요.
자리 등록 · slots
POST https://metamoa.com/ads/v1/slots
{
"space": "{space_id}",
"key": "{mm_adpub_…}",
"name": "plaza-front",
"kind": "floor",
"position": [
0,
0,
-8
],
"rotation": [
0,
90,
0
],
"size": [
4,
4,
4
],
"scale": [
0.5,
2
],
"industries": [
"game",
"food"
]
}
→ 201 { "slot": {…, "enabled": false} } (새 자리, 공간운영자가 켤 때까지 꺼짐) · 200 (같은 이름의 자리를 바꾸지 않고 그대로)
이벤트 보내기 · events
POST https://metamoa.com/ads/v1/events
{
"space": "onsen-village",
"key": "mm_adpub_XXXXXXXXXXXXXXXXXXXXXX",
"events": [
{
"type": "imp",
"token": "<serve 토큰>",
"ms": 2100,
"area": 0.04,
"inside": 1,
"vis": true,
"focus": true,
"inputs": 3,
"wd": false
},
{
"type": "time",
"token": "<serve 토큰>",
"ms": 5000,
"vis": true,
"inputs": 2,
"wd": false
},
{
"type": "click",
"token": "<serve 토큰>",
"trusted": true,
"wd": false
}
]
}
→ 200 { "accepted": 3, "excluded": 0 }
| type | 칸 | 뜻 |
|---|---|---|
| imp | token, ms, area, inside, vis, focus, inputs, wd | ms: 이어서 보인 시간, area: 화면 넓이 대비 모델 넓이(0~1), inside: 모델 중 화면 안 몫(0~1), vis·focus: 탭 보임·창 포커스, inputs: 그동안 카메라·포인터 입력 수, wd: navigator.webdriver |
| time | token, ms, vis, inputs, wd | 지난 time 뒤로 보인 시간(15,000ms까지)과 그 사이 입력 수 |
| click | token, trusted, wd | 광고를 탭·클릭함. trusted: 사람 입력(isTrusted) |
한 번에 20개까지. 이벤트에는 vid를 보내지 않아요. 보는 사람은 토큰 안의 값을 써요. 틀린 이벤트·남의 공간 토큰은 excluded로만 세요.
입장 · go
입장은 메타모아 도메인의 /ads/go에서 Cloudflare Turnstile로 사람인지 확인한 뒤에만 세요(키가 있을 때 작은 확인 화면이 1~2초 보여요). 확인이 실패해도 사람은 브랜드 공간으로 가고 입장만 세지 않아요.
GET https://metamoa.com/ads/go?t={serve 토큰}
→ 302 Location: {brand_url} (노출이 센 토큰이면 입장을 센 뒤 브랜드 공간으로. 캠페인이 집행 중·기간 안이고 토큰이 유효 시간 안일 때만 검수에서 승인된 brand_url로 가고, 아니면 Location: /)
광고용 건물·캐릭터와 업로드 규격
모델이 없는 광고주는 우리가 주는 광고용 건물이나 기본 광고 캐릭터를 골라요. 건물은 두 방식 중 하나를 골라요: 이미지형(정해진 판에 이미지 한 장씩) · 텍스처형(건물 표면 전체에 텍스처·패턴 한 장).
텍스처형은 기본 원판(정사각 PNG)을 내려받아 고친 뒤 같은 크기(1024~2048px)로 올려요. 선 있는 원판의 분홍 선은 표면 조각 경계라, 조각 안쪽만 칠하면 그 표면에 나와요.
기본 광고 캐릭터는 금액 상관없이 무료예요. character 자리에 서거나 걸어 다니며, 목걸이 팻말·사원증·손 피켓·풍선·깃발·쇼핑백 같은 소품을 뼈에 붙여 들고, 소품 판에 광고 이미지나 간판 글자(12자까지)가 들어가요. 고양이·강아지·빈센트는 목에 거는 소품만 돼요.
| 캐릭터 | id | 들 수 있는 소품 | 라이선스 |
|---|---|---|---|
| 창모아 | changmoa | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 메타뼈스 | metabbyeos | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 벌크업로더 | bulkuploader | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 핫스팟 법사 | hotspot | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 방장기사 | bangjang | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 털갈이드 | teolgal | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 네일버스 | neilbus | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 링크블린 | linkblin | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 디아블롱 | diablong | prop-neck-sign prop-badge prop-picket prop-balloon prop-flag prop-shopping-bag | CC0 1.0 |
| 빈센트 | vincent | prop-neck-sign prop-badge | 자사 (플로트 주식회사) |
| 냥줍고 단모 고양이 | cat_short | prop-neck-sign prop-badge | CC BY 4.0 |
| 냥줍고 장모 고양이 | cat_long | prop-neck-sign prop-badge | CC BY 4.0 |
| 냥줍고 먼치킨 | cat_munchkin | prop-neck-sign prop-badge | CC BY 4.0 |
| 냥줍고 코기 | dog_corgi | prop-neck-sign prop-badge | CC BY 4.0 |
| 소품 | id | 이미지 비율·최소 크기 |
|---|---|---|
| 목걸이 간판(사원증형) | prop-badge | 720:1024, 360×512px 이상 |
| 풍선 | prop-balloon | 1024:1024, 512×512px 이상 |
| 손 깃발 | prop-flag | 1024:688, 512×344px 이상 |
| 목에 거는 팻말 | prop-neck-sign | 1024:720, 512×360px 이상 |
| 손 피켓 | prop-picket | 1024:720, 512×360px 이상 |
| 쇼핑백 | prop-shopping-bag | 1024:1024, 512×512px 이상 |
- 이미지 형식: PNG · JPEG · WEBP, 2MB까지, 가로·세로 4096px까지, 비율 오차 2%까지. 올린 이미지는 WebP로 다시 저장해요(사진 정보는 지워져요).
- 3D 모델: glTF 2.0 바이너리(.glb) 한 파일, 6MB까지, 텍스처는 파일 안에, 바깥 주소(uri) 없이.
- 올린 파일은 내용(매직 바이트)으로 형식을 확인해요: 이미지는 PNG·JPEG·WebP만(SVG·HTML 등은 받지 않음) 받아 WebP로 다시 만들고, 모델은 glTF 2.0 바이너리만 받아요. 광고 파일은 고정 형식(image/webp, model/gltf-binary)·nosniff·sandbox 정책으로만 내보내 스크립트로 실행되지 않아요.
- 광고 모델의 크기는 공간에 따라 공간운영자·메타모아가 조절할 수 있어요.
- 모델·캐릭터 파일은 /ads/models/ 아래에 있고, 스크립트가 허용되지 않는 응답(고정 형식·nosniff·sandbox)으로만 나가요. 목록과 출처는 docs/ads/MODELS.md, 바꿀 때는 scripts/ads-import-models.mjs로 data/ads-buildings.json과 파일을 함께 다시 만들어요.
모델 출처 표기
기본 모델은 CC0(퍼블릭 도메인)·자사 제작이 대부분이고, 아래는 CC BY라 출처를 밝혀요. 모두 플로트 주식회사가 크기·광고 면 등을 고쳤어요.
- 세움 빌보드: "Blank Billboard" by Jarlan Perez (https://poly.pizza/m/1E0d2B7bC8e), CC BY 3.0 (https://creativecommons.org/licenses/by/3.0/), 플로트 주식회사가 고침
- 팝업 부스: "Carnival Booth" by sirkitree (https://poly.pizza/m/4NizXJZsuO2), CC BY 3.0 (https://creativecommons.org/licenses/by/3.0/), 플로트 주식회사가 고침
- 버스 정류장 광고판: "Bus Stop" by Zsky (https://poly.pizza/m/OWczicNWhO), CC BY 3.0 (https://creativecommons.org/licenses/by/3.0/), 플로트 주식회사가 고침
- A형 입간판: "Sandwich Board" by Anonymous (https://poly.pizza/m/ejy0W_7cHWT), CC BY 3.0 (https://creativecommons.org/licenses/by/3.0/), 플로트 주식회사가 고침
- 냥줍고 단모 고양이: "Cat" by Polyfork (https://polyfork.dev/asset/cat-base), CC BY 4.0, 플로트 주식회사가 복셀로 고침
- 냥줍고 장모 고양이: "Cat" by Polyfork (https://polyfork.dev/asset/cat-base), CC BY 4.0, 플로트 주식회사가 복셀로 고침
- 냥줍고 먼치킨: "Cat" by Polyfork (https://polyfork.dev/asset/cat-base), CC BY 4.0, 플로트 주식회사가 복셀로 고침
- 냥줍고 코기: "Dog" by Polyfork (https://polyfork.dev/asset/dog-base), CC BY 4.0, 플로트 주식회사가 복셀로 고침
모델 제작 요청
광고주가 「모델 제작 요청」을 고르면 우리가 3D 모델을 만들어 캠페인에 붙인 뒤 검수해요. 만들 자료(제품 사진·로고·도면)는 [email protected]로 캠페인 번호와 함께 보내 주세요.
| 등급 | grade | 제작비 | 이 매체비 이상 집행하면 무료 |
|---|---|---|---|
| 단순 제품·간판 | simple | 50,000원 (공급가, 부가세 별도) | 500,000원 |
| 정밀 제품 | precise | 100,000원 (공급가, 부가세 별도) | 1,000,000원 |
| 새 캐릭터 | character | 100,000원 (공급가, 부가세 별도) | 1,000,000원 |
- 무료 기준은 실제 집행 매체비(계약 전체 기간 합, 최대 3개월, 모델링·부가세·무효 트래픽 제외)로 봐요.
- 무료로 만든 모델은 메타모아 송출용 사용권만 드리고(저작권은 메타모아, 양도는 제작비를 받고), 수정은 1회예요. 광고주 사정으로 해지·감액하면 제작비를 비례로 청구해요: 제작비 × (1 − 실제 집행 매체비 ÷ 무료 기준). 유료 모델은 수정 2회예요.
- 기본 광고 캐릭터(모아 야영지·냥줍고 캐릭터)는 제작 요청 없이 금액 상관없이 무료로 써요.
광고주: 셀프 등록 순서
- https://metamoa.com/ads/manage에서 메타모아 계정으로 로그인(가입도 같은 창).
- 캠페인 만들기: 이름·브랜드·업종, 브랜드 공간 주소(입장 CPA를 쓸 때).
- 광고물: ① 내 3D 모델(glb) 올리기 ② 광고용 건물 고르기 → 이미지형이면 판마다 이미지, 텍스처형이면 텍스처 한 장 ③ 기본 광고 캐릭터 + 소품 고르기 → 소품 판에 이미지나 간판 글자 ④ 모델 제작 요청(등급 선택). 규격과 미리보기가 보여요.
- 노출 공간: 광고 게재에 동의한 공간 목록에서 분류·지금 접속자 수·하루 공간 클릭을 보고 자리를 직접 고르거나, 「추천으로」를 누르면 업종·분류·트래픽을 맞춘 공간 묶음을 추천해요.
- 과금 방식(CPM·CPA·노출 시간·공간 고정 계약)·예산·기간을 정하고 신청 → 검수 대기.
- 검수(음란·폭력·저작권 의심 등)를 통과하면 집행돼요. 진행 중인 캠페인은 멈추고 다시 켤 수 있고, viewable 노출·클릭·입장·보인 시간을 날마다 봐요.
PUT https://metamoa.com/api/ads/assets?kind=image&building=billboard-low&face=main (본문: 이미지 파일)
PUT https://metamoa.com/api/ads/assets?kind=image&prop=prop-neck-sign (본문: 이미지 파일)
POST https://metamoa.com/api/ads/recommend {"industry": "food", "count": 5}
POST https://metamoa.com/api/ads/campaigns
{
"name": "겨울 우유 3D",
"brand": "반짝 우유",
"industry": "food",
"billing": "cpm",
"budget_won": 500000,
"start_day": "2026-12-01",
"end_day": "2026-12-31",
"targeting": "recommend",
"slot_ids": [
"sl_k2m9x0a1b2c3d4e5"
],
"brand_url": "https://milk.example/space",
"creative": {
"kind": "building",
"building_id": "billboard-low",
"mode": "image",
"images": {
"main": "as_a1b2c3d4e5f6g7h8"
}
}
}
(헤더: X-Account-Token: mm1.… · X-Device-Id: <기기 id>)
캐릭터 광고물: {"kind": "character", "character_id": "bangjang", "prop": "prop-picket", "image": "as_…"} 또는 "image" 대신 "text": "반짝 우유". 공간 고정 계약: "billing": "fixed", "fixed_period": "week" | "month", end_day = start_day + 기간 − 1. 모델 제작 요청: {"kind": "request", "grade": "simple", "note": "…"}.
검수
- 올라온 모델·이미지와 캠페인은 게재 전에 검수 대기(review)예요. 관리자가 승인하면 집행(active), 반려하면 rejected와 이유가 남아요.
- 이미지는 올릴 때 AI 이미지 검사로 음란·폭력·남의 로고·캐릭터를 먼저 보고, 걸리면 바로 막아요(rejected). 검사를 못 하면 관리자가 직접 봐요.
- 모델은 관리자가 직접 열어 봐요. 음란·폭력·저작권 의심이면 반려해요.
오류 코드
오류 본문은 { "error": "<코드>", "message": "<다음에 할 일 한 줄>" }이고, 칸 오류는 fields, 한도는 retry_after가 같이 와요.
| HTTP | error | 뜻 |
|---|---|---|
| 400 | invalid | 칸 형식이 틀림. fields에 틀린 칸이 온다. |
| 400 | bad_model | glb가 아님, glTF 2.0이 아님, 바깥 주소(uri)가 들어 있음. reason에 이유. |
| 400 | bad_image | 이미지 형식·크기·비율이 규격과 다름. reason(format·bytes·too_large·too_small·aspect)과 spec이 온다. |
| 400 | bad_building | 자리의 default_building이 GET /ads/v1/buildings의 건물·팻말 id가 아님(빈 값은 됨). |
| 400 | slot_unavailable | 고른 자리가 광고를 받지 않거나, 그 업종을 받지 않거나, 광고물 종류와 맞지 않음(캐릭터는 캐릭터 자리에만). slot_ids에 그 자리. |
| 401 | unauthorized | 공간 키·공간 관리 로그인이 없거나 틀림. |
| 401 | sign_in | 광고주: 메타모아 계정 로그인이 없거나 끝남. |
| 403 | owner_required | 공간 키는 있지만 공간운영자 확인 전. |
| 403 | owner_page_only | 약관 확인·광고 게재 동의를 공간 키로 시도함(공간 관리 화면에서만 된다). |
| 403 | bad_ad_key | SDK: space와 key(mm_adpub_)가 맞지 않음. |
| 403 | terms_required | 그 공간이 광고 게재 약관 확인을 하지 않음. |
| 403 | ads_off | 그 공간이 광고 게재 동의를 끔. |
| 403 | origin_mismatch | SDK 요청이 그 공간 사이트(같은 도메인)에서 오지 않음. |
| 403 | not_yours | 광고주: 남의 파일 id를 씀. |
| 404 | no_slot | 그 이름·id의 광고 자리가 없음. |
| 404 | not_found | 그 캠페인·파일이 없음. |
| 409 | not_listed | 목록에 오르지 않은(승인 전·숨김) 공간. |
| 409 | payout_info_required | 광고 게재 동의를 켜려는데 정산 정보의 유형(개인·사업자)과 연락처(휴대전화나 이메일)가 없음. 공간 관리 수익 창출(/owner/revenue?id=<공간 id>)에서 넣는다. |
| 409 | advertiser_required | 광고주: 광고주 정보(PUT /api/ads/advertiser)를 넣기 전에 캠페인을 신청하거나 결제하려 함. |
| 409 | biz_doc_required | 광고주: 사업자 광고주가 사업자등록증 사본(PUT /api/ads/advertiser/doc)을 올리기 전에 결제하거나 공간 고정 계약을 신청함. |
| 409 | business_only | 광고주: 개인 광고주가 사업자등록증 사본을 올리려 함. |
| 400 | bad_doc | 광고주: 사업자등록증 사본이 PDF·JPG·PNG가 아님. |
| 413 | doc_too_big | 광고주: 사업자등록증 사본이 5MB를 넘음. |
| 503 | advertiser_unavailable | 광고주: 서버에 광고주 정보 암호화 키가 없어 받지 못함. |
| 409 | email_not_linked | 광고주: 개인 광고주의 이메일이 이 메타모아 계정에 로그인한 메일이 아님. |
| 409 | name_taken | 같은 이름의 자리가 이미 있음. |
| 409 | slot_limit | 공간마다 공간운영자 자리 30개까지, 확인 안 된 SDK 자리 5개까지. |
| 409 | slot_taken | 그 기간에 다른 공간 고정 계약이 잡힌 자리. |
| 409 | tier_too_low | 하루 공간 클릭이 가장 낮은 등급에 못 미치는 공간의 자리를 공간 고정 계약으로 고름. slot_ids에 그 자리. |
| 409 | not_for_sale | 공간 고정 계약 단가가 없음. |
| 409 | under_minimum | 최소 집행 매체비보다 적음. min_spend_won, spend_won이 온다. |
| 409 | no_click_history | 공간 클릭 이력이 아직 없는 공간의 자리를 CPM·공간 고정 계약으로 고름(노출량·등급을 잴 수 없음). CPA·노출 시간형만 된다. slot_ids, billings가 온다. |
| 409 | over_inventory | CPM 예산이 고른 자리의 예상 viewable 노출 상한을 넘음. max_budget_won이 온다. |
| 409 | rejected_file | 검수에서 막힌 파일을 캠페인에 씀. |
| 409 | wrong_state | 그 상태에서 못 하는 일(검수 중 캠페인 멈추기 등). |
| 409 | stale | 관리자: 검수 화면을 연 뒤 캠페인이 바뀜(decide의 version이 지금 것과 다름). 새로 보고 다시 결정. |
| 409 | already_paid | 이미 결제한 캠페인에 결제를 다시 열려고 함. |
| 409 | not_paid | 관리자: 결제가 끝나지 않은 캠페인의 정산을 승인하려 함. |
| 409 | held | 관리자: 보류 중인 몫이 있는 캠페인의 정산을 승인하려 함(보류를 먼저 판정). |
| 424 | hrum_failed | 결제(흐름)에 연결하지 못함. 잠시 뒤 다시. |
| 503 | pay_not_ready | 결제를 아직 열 수 없음(서버 설정 전). |
| 409 | model_missing | 관리자: 모델 제작 요청 캠페인에 모델을 붙이기 전에 승인함. |
| 413 | model_too_big | 모델 파일이 6MB를 넘음. |
| 429 | too_many | 요청이 너무 많음. Retry-After 초만큼 기다린다. |
전체 경로
SDK (CORS *, 그 공간 사이트에서)
GET /ads/sdk.js · GET /ads/v1/serve · POST /ads/v1/slots · POST /ads/v1/events · GET /ads/go (Turnstile 화면) · POST /ads/go · GET /ads/v1/buildings · GET /ads/a/{asset}.glb|webp
공간운영자 (공간 관리 로그인 mm_own_ 또는 공간 키 mm_live_)
GET /api/space/{id}/ads · POST /api/space/{id}/ads/terms · POST /api/space/{id}/ads/consent {"on": true} (이 둘은 공간 관리 로그인만)
GET|POST /api/space/{id}/ads/slots · PATCH|DELETE /api/space/{id}/ads/slots/{slot_id} · GET /api/space/{id}/ads/stats?from=&to=
공간운영자 등급 (공개)
GET /api/ads/partners → 등급표, 남은 자리(플래티넘·골드) · 안내 페이지 /ads/partners
광고주 (메타모아 계정: X-Account-Token + X-Device-Id)
GET|PUT /api/ads/advertiser (광고주 정보: 사업자 또는 개인, 캠페인 신청·결제 전에 한 번) · PUT /api/ads/advertiser/doc (사업자등록증 사본, 첫 결제·공간 고정 계약 전) · POST /api/ads/production (제작 의뢰: 이름·회사, 연락처, 원하는 것, 예산 범위(선택), 일정 · 로그인 없이) · GET /api/ads/options · GET /api/ads/spaces · POST /api/ads/recommend · PUT /api/ads/assets · GET /api/ads/assets · GET /api/ads/assets/{id}/raw
GET|POST /api/ads/campaigns · GET /api/ads/campaigns/{id} · PATCH /api/ads/campaigns/{id} {brand_url} (바꾸면 다시 검수 대기, 승인 전까지 입장은 전에 승인된 주소로) · POST /api/ads/campaigns/{id}/pause|resume · POST /api/ads/campaigns/{id}/pay (흐름 결제 열기)
문서: /ads/docs (사람) · /ads/llms.txt (AI) · /api#ads · /api/openapi.json
문의 · Contact
[email protected] · 메타모아 · 플로트 주식회사 (FLOAT Co., Ltd.)
이 문서 · This page: https://metamoa.com/api (HTML) · https://metamoa.com/api.md (Markdown)
메타모아 앱 받기 · Get the app: App Store · Google Play