Create Your First Template

Learn how to define a template with properties, actions, and faces.

10 minBeginner

Prerequisites

  • A DUAL account with an organization
  • An API key or JWT token

What You'll Build

In this tutorial you'll create a template — the blueprint that defines the structure, properties, and behaviour of tokenized objects on the DUAL network. By the end you'll have a working template you can mint objects from.

Step 1 — Authenticate

DUAL uses a three-step OTP authentication flow. First, request a one-time password sent to your email:

bash
# Step 1a — Request OTP
curl -X POST https://gateway-48587430648.europe-west6.run.app/auth/otp \
  -H "Content-Type: application/json" \
  -d '{ "email": "dev@example.com" }'

Check your inbox for the 6-digit OTP code, then log in:

bash
# Step 1b — Login with OTP (returns a system-scoped JWT)
curl -X POST https://gateway-48587430648.europe-west6.run.app/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "dev@example.com",
    "otp": "123456"
  }'

The response includes an access_token (system-scoped). To perform actions within an organization, switch to an org context:

bash
# Step 1c — Switch to organization context (returns org-scoped JWT)
curl -X POST https://gateway-48587430648.europe-west6.run.app/organizations/switch \
  -H "Authorization: Bearer $SYSTEM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "id": "your-org-id" }'

Save the org-scoped access_token as an environment variable — you'll use it in every subsequent request:

bash
export DUAL_TOKEN="eyJhbGciOiJIUzI1NiIs..."

Step 2 — Create the Template

Templates define the schema for your tokenized objects. Each template belongs to an organization and specifies properties, actions, and visual faces.

bash
curl -X POST https://gateway-48587430648.europe-west6.run.app/templates \
  -H "Authorization: Bearer $DUAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "template": {
      "name": "my-org::loyalty-card::v1",
      "description": "A digital loyalty card template",
      "public": false,
      "cloneable": false,
      "properties": {
        "points": 0,
        "tier": "silver",
        "holder_name": ""
      }
    }
  }'

The template name follows the convention org::name::version. Properties define the default data fields that every object minted from this template will inherit.

Step 3 — Verify Your Template

Confirm the template was created by listing your organization's templates:

bash
curl https://gateway-48587430648.europe-west6.run.app/templates \
  -H "Authorization: Bearer $DUAL_TOKEN"

You should see your new template in the response array with all the properties you defined.

Step 4 — Add a Face

Faces are the visual layer of your template. They can be images, 3D models, or web views. Let's attach a simple image face:

bash
curl -X POST https://gateway-48587430648.europe-west6.run.app/faces \
  -H "Authorization: Bearer $DUAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "face": {
      "template": "my-org::loyalty-card::v1",
      "display_type": "ResourceFace",
      "meta": {
        "image": "https://your-cdn.com/loyalty-card.png"
      },
      "is_default": true
    }
  }'

What's Next?

Your template is ready. In the next tutorial, Mint & Transfer Objects, you'll create object instances from this template and transfer them between wallets.

Tip: You can update a template's properties at any time using PATCH /templates/{id}, but be aware that changes won't retroactively update objects already minted from it.