Skip to main content

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: female or male
  • outfitMode: template or user

Send the input image using exactly one of these fields:

  • faceFile: user face image file
  • faceImageBase64: 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, or image/webp

Do not send faceFile and faceImageBase64 in the same request.

Optional fields

  • outputFormat: sheet_url or panel_base64

If omitted, outputFormat defaults to sheet_url.

Supported image types

  • image/jpeg
  • image/png
  • image/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 image
  • sheet.width: width of the generated sheet image
  • sheet.height: height of the generated sheet image
  • panels.front: crop rectangle for the front view
  • panels.rightThreeQuarter: crop rectangle for the right three-quarter view
  • panels.rightProfile: crop rectangle for the right profile view
  • panels.back: crop rectangle for the back view
  • panelImages.front.base64: optional base64 image for the front view
  • panelImages.rightThreeQuarter.base64: optional base64 image for the right three-quarter view
  • panelImages.rightProfile.base64: optional base64 image for the right profile view
  • panelImages.back.base64: optional base64 image for the back view

6. Error Responses

CodeMeaning
400Invalid request data, invalid base64 image data, or invalid multipart form data
401Authentication failed
411Content-Length header is required
413Request body exceeds 2 MB or decoded input image exceeds 1 MB
415Unsupported image type
500Avatar generation failed

Example error body:

{
"error": "Image must be smaller than 1MB"
}

7. Billing

Avatar generation is a billable API action.

FieldValue
ProductAvatar API
Route keyavatar.generate
Billable unitSuccessful avatar generation
Credit cost1 credit per successful generation
Charge ruleCharged 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 faceFile or faceImageBase64, not both.
  • Keep the real input image within the 1 MB limit.
  • 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 sheetUrl as 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 outputFormat is panel_base64, the returned panel base64 strings are derived from the generated sheet. GOFA still stores only the generated sheet image.