Avatar API
This document describes how to call the GOFA Avatar API from a user-facing web application.
The Avatar API accepts a face image and returns:
- a temporary URL for the generated avatar sheet
- the sheet dimensions
- four panel crop rectangles so the frontend can render the front, right three-quarter, right profile, and back views from the same sheet image
- optionally, four separated panel images as base64 strings
1. Endpoint
POST /api/avatar
Call this endpoint on your assigned GOFA client domain.
2. Authentication
The Avatar API supports two integration patterns.
Option A: User-facing frontend integration
Use this option when the end user signs in through GOFA/Firebase authentication and the browser or mobile app calls the Avatar API directly.
Include the Firebase ID token in the request header:
Authorization: Bearer <Firebase ID Token>
The Firebase ID token identifies the signed-in end user. GOFA uses this token to validate access, associate the request with the user, and record API usage for the client.
Option B: Server-to-server integration
Use this option when your website or app has its own authentication system and your backend calls GOFA on behalf of your users.
First, create an API key in the GOFA B2B portal. Store this API key only on your backend server. Do not expose it in browser or mobile app code.
Then exchange the API key for a temporary ClientToken:
POST /api/client/token
Content-Type: application/json
{
"clientId": "your-client-id",
"clientSecret": "your-api-key"
}
Successful response:
{
"token": "client-token",
"apiKeyId": "api-key-id",
"expiresAt": "2026-06-04T13:00:00.000Z"
}
Use the returned token when calling the Avatar API from your backend:
ClientToken: <client-token>
The API key is used only to obtain a ClientToken. It is not sent directly to the Avatar API.
3. Request Format
Send the request as multipart/form-data.
Required fields
templateGender:femaleormaleoutfitMode:templateoruser
Send the input image using exactly one of these fields:
faceFile: user face image filefaceImageBase64: user face image as a base64 string or data URL
If faceImageBase64 is a raw base64 string without a data:image/...;base64, prefix, also send:
faceImageMediaType:image/jpeg,image/png, orimage/webp
Do not send faceFile and faceImageBase64 in the same request.
Optional fields
outputFormat:sheet_urlorpanel_base64
If omitted, outputFormat defaults to sheet_url.
Supported image types
image/jpegimage/pngimage/webp
Size limits
- Maximum real input image size:
1 MB - Maximum request body size:
2 MB
For base64 input, GOFA decodes the base64 string and applies the 1 MB limit to the decoded image bytes. The larger 2 MB request body limit exists only to allow for base64 encoding overhead.
4. Request Example
Frontend example with Firebase ID token
const idToken = await auth.currentUser?.getIdToken();
const formData = new FormData();
formData.append("faceFile", file);
formData.append("templateGender", "female");
formData.append("outfitMode", "template");
formData.append("outputFormat", "sheet_url");
const response = await fetch("/api/avatar", {
method: "POST",
headers: {
Authorization: `Bearer ${idToken}`,
},
body: formData,
});
const data = await response.json();
Backend example with ClientToken
const tokenResponse = await fetch("https://www.uat.gofa.app/api/client/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
clientId: "your-client-id",
clientSecret: process.env.GOFA_API_KEY,
}),
});
const { token } = await tokenResponse.json();
const formData = new FormData();
formData.append("faceFile", file);
formData.append("templateGender", "female");
formData.append("outfitMode", "template");
formData.append("outputFormat", "panel_base64");
const response = await fetch("https://www.uat.gofa.app/api/avatar", {
method: "POST",
headers: {
ClientToken: token,
},
body: formData,
});
const data = await response.json();
Backend example with base64 input
const formData = new FormData();
formData.append("faceImageBase64", "data:image/png;base64,iVBORw0KGgo...");
formData.append("templateGender", "female");
formData.append("outfitMode", "template");
formData.append("outputFormat", "panel_base64");
const response = await fetch("https://www.uat.gofa.app/api/avatar", {
method: "POST",
headers: {
ClientToken: token,
},
body: formData,
});
const data = await response.json();
5. Successful Response
200 OK
{
"sheetUrl": "https://...",
"sheet": {
"width": 1024,
"height": 1024
},
"panels": {
"front": {
"left": 20,
"top": 20,
"width": 472,
"height": 472
},
"rightThreeQuarter": {
"left": 532,
"top": 20,
"width": 472,
"height": 472
},
"rightProfile": {
"left": 20,
"top": 532,
"width": 472,
"height": 472
},
"back": {
"left": 532,
"top": 532,
"width": 472,
"height": 472
}
},
"panelImages": {
"front": {
"base64": "data:image/jpeg;base64,...",
"mediaType": "image/jpeg",
"width": 472,
"height": 472
},
"rightThreeQuarter": {
"base64": "data:image/jpeg;base64,...",
"mediaType": "image/jpeg",
"width": 472,
"height": 472
},
"rightProfile": {
"base64": "data:image/jpeg;base64,...",
"mediaType": "image/jpeg",
"width": 472,
"height": 472
},
"back": {
"base64": "data:image/jpeg;base64,...",
"mediaType": "image/jpeg",
"width": 472,
"height": 472
}
}
}
The panelImages object is returned only when outputFormat is panel_base64.
Response fields
sheetUrl: temporary URL of the generated avatar sheet imagesheet.width: width of the generated sheet imagesheet.height: height of the generated sheet imagepanels.front: crop rectangle for the front viewpanels.rightThreeQuarter: crop rectangle for the right three-quarter viewpanels.rightProfile: crop rectangle for the right profile viewpanels.back: crop rectangle for the back viewpanelImages.front.base64: optional base64 image for the front viewpanelImages.rightThreeQuarter.base64: optional base64 image for the right three-quarter viewpanelImages.rightProfile.base64: optional base64 image for the right profile viewpanelImages.back.base64: optional base64 image for the back view
6. Error Responses
| Code | Meaning |
|---|---|
| 400 | Invalid request data, invalid base64 image data, or invalid multipart form data |
| 401 | Authentication failed |
| 411 | Content-Length header is required |
| 413 | Request body exceeds 2 MB or decoded input image exceeds 1 MB |
| 415 | Unsupported image type |
| 500 | Avatar generation failed |
Example error body:
{
"error": "Image must be smaller than 1MB"
}
7. Billing
Avatar generation is a billable API action.
| Field | Value |
|---|---|
| Product | Avatar API |
| Route key | avatar.generate |
| Billable unit | Successful avatar generation |
| Credit cost | 1 credit per successful generation |
| Charge rule | Charged only after the avatar sheet is generated successfully |
Requests that fail before successful avatar generation, such as invalid input, unsupported file type, authentication failure, quota failure, or AI generation failure, are not charged. These requests may still be recorded in API usage logs for reporting and troubleshooting.
8. Frontend Rendering Strategy
By default, the response does not return four separate image URLs.
Instead:
- the backend returns one generated sheet image URL
- the backend also returns crop rectangles for each panel
- the frontend displays the same image multiple times using different crop areas
This keeps storage usage low and avoids creating additional temporary panel files.
If you need separated panel image data, set:
outputFormat=panel_base64
GOFA will still store only one generated sheet image, but the response will include four derived panel images as base64 strings.
9. Frontend Example: Render the Avatar Sheet
<img
src={data.sheetUrl}
alt="Generated avatar sheet"
width={data.sheet.width}
height={data.sheet.height}
/>
10. Frontend Example: Render a Panel Using Canvas
function renderPanel(
canvas: HTMLCanvasElement,
image: HTMLImageElement,
rect: { left: number; top: number; width: number; height: number }
) {
const context = canvas.getContext("2d");
if (!context) {
return;
}
canvas.width = rect.width;
canvas.height = rect.height;
context.clearRect(0, 0, rect.width, rect.height);
context.drawImage(
image,
rect.left,
rect.top,
rect.width,
rect.height,
0,
0,
rect.width,
rect.height,
);
}
Example usage:
const image = new Image();
image.onload = () => {
renderPanel(frontCanvas, image, data.panels.front);
renderPanel(rightThreeQuarterCanvas, image, data.panels.rightThreeQuarter);
renderPanel(rightProfileCanvas, image, data.panels.rightProfile);
renderPanel(backCanvas, image, data.panels.back);
};
image.src = data.sheetUrl;
11. Notes
- For frontend integrations, call this API with a valid Firebase ID token for the signed-in user.
- For server-to-server integrations, call this API with a valid ClientToken obtained from
/api/client/token. - Keep API keys on your backend server only. Do not expose API keys in browser or mobile app code.
- Always use
multipart/form-data. - Send either
faceFileorfaceImageBase64, not both. - Keep the real input image within the
1 MBlimit. - Base64 input is accepted to support clients that cannot send binary file fields. The decoded image still must be within
1 MB. - The frontend should treat
sheetUrlas a temporary resource and should not assume it is permanent. - The panel coordinates are based on the generated sheet and should be used together with the returned
sheetUrl. - If
outputFormatispanel_base64, the returned panel base64 strings are derived from the generated sheet. GOFA still stores only the generated sheet image.