Skip to main content

Integrating Yats in your applications

caution

The integration libraries and documentation for Yat are still in Alpha. They are not yet feature complete, and there are likely bugs in the implementation.

This guide will walk you through a full integration flow, covering the following bases:

This is the code to carry out this flow in full, using the Yat SDK libraries. It might look like a lot all in one go, but fear not. We'll walk through each piece step by step in the following sections.

const yat = require("yatJs");const api = new yat.YatJs();
// hint: You can specify an alternative API url with the basePath property, e.g.:// api.basePath = 'http://localhost:3001';
// Note: on first attempt checkout will fail if registering a user// Please login and confirm the email and then rerun the democonst email = "[email protected]";const password = "yat-secret";
/** * Register a new Yat account * @returns {Promise<boolean>} */async function register() {    let details = yat.RegisterUserParameters.constructFromObject({        first_name: "Testy",        last_name: "McTesty",        email: email,        password: password,    });
    try {        await api.users().createUser(details);
        console.log("Registered user")        return true;    } catch (err) {        const alreadyRegistered = err.status === 422 && err.body.fields.email[0].code === "uniqueness";        if (!alreadyRegistered) {            console.log(`Could not register an account: ${err.error}`);        } else {            console.log(`User was already registered, continuing`);        }        return alreadyRegistered;    }}
/** * Generate a random yat of length `len` from the given list of emoji * @returns {string} */function selectRandomYat(list, len) {    const yat = [];    for (let i = 0; i < len; i++) {        const index = Math.floor(Math.random() * list.length);        yat.push(list[index]);    }    return yat.join("");}
/** * Login into the yat API * @returns {Promise<void>} */async function login() {    try {        let res = await api.login(email, password);    } catch (res) {        console.log(`Could not log in: ${res.error}`);        throw new Error("Could not login");    }}
/** * Attempt to procure a yat using the given promo code * @returns {Promise<string>} */async function purchaseYat() {    // Request the set of supported emoji    const emojiList = await api.emoji().emojiList();    // Clear the cart    await api.cart().clearCart();    // This is for demo purposes. There are also endpoints for automatically selecting a random yat and applying a    // promo code.    const myYat = selectRandomYat(emojiList, 4);    console.log(`Checking ${myYat} availability...`);    let opts = {        'redemptionCode': "FREEYAT" // String | Redemption code    };    const yatInfo = await api.emojiID().searchEmojiID(myYat, opts);    console.log(yatInfo.result);    if (!yatInfo.result.available) {        console.log(`Bad luck :(, ${yat} is not available.`);    }        // Add the yat to the cart. This time use the constructor    const order = new yat.AddItemsCartRequest([        {            emoji_id: myYat,        }    ]);    const cart = await api.cart().addItems(order);    console.log("Order added to cart: ", cart);    // Checkout, currently to add a promo method the cart must be in a pending payment state    await api.cart().checkout({ method: "Stripe"});        let promoCodeResult = await api.cart().applyPromoCode({        'code': "FREEYAT" // String | Redemption code    });    console.log("Apply promo code request succeeded: ", promoCodeResult);
    let checkoutFreeResult = await api.cart().checkout({ method: "Free" });    console.log("Checkout succeeded: ", checkoutFreeResult);    await new Promise(r => setTimeout(r, 5000));
    return myYat;}
/** * List the yats the user owns * @returns {Promise<*>} */async function getMyYats() {    let yats = await api.emojiID().listEmojiIDs();    console.log("These are my yats: ", yats);    return yats;}
/** * Add a url record to my Yat * @returns {Promise<*>} */async function addYatRecord(yat, url) {    const req = {        insert: [{            tag: "0x4001",            data: url        }]    };    try {        await api.emojiID().editEmojiID(yat, req);        console.log("URL added to yat.");    } catch (err) {        console.log("Error Result of adding record request: ", err.body);    }    return yat;}
/** * Display all the records associated with the given yat * @param yat * @returns {Promise<void>} */async function printYatRecords(yat) {    try {        let records = await api.emojiID().lookupEmojiID(yat);        console.log(records);    } catch (err) {        console.log("Error fetching yat data: ", err.body)    }}
// Main scriptregister().then(login).then(getMyYats).then(yats => {    if (yats.length === 0) return purchaseYat();    return yats[0];}).then(yat => {    return addYatRecord(yat, "http://api-docs.y.at/docs/sdks/nodejs/sdk_nodejs_index");}).then(printYatRecords).catch(res => {    console.log('Error:', JSON.stringify(res.body));}).then((res) => {    console.log("Bye!")});

This script produces output similar to

User was already registered, continuingThese are my yats:  []Checking ๐Ÿ’ผ๐ŸŒฒ๐Ÿ”ฉ๐Ÿ’ availability...SearchResultResult {  availability: 'Available',  available: true,  emoji_id: '๐Ÿ’ผ๐ŸŒฒ๐Ÿ”ฉ๐Ÿ’',  length: 4,  rhythm_score: 14,  stats: [    EmojiStatsResponseMetrics {      description: 'Number of times emoji was looked up via API',      finish_date: 2021-11-03T22:59:02.773Z,      key: '๐Ÿ’ผ๐ŸŒฒ๐Ÿ”ฉ๐Ÿ’',      metric: 'api_emoji_lookups',      start_date: 2021-10-06T22:59:02.773Z,      value: 0    }  ],  copy: {    description: '<h3>Every Yat is one-of-a-kind, and is pay once, own forever. The price is based on its Rhythm Score (RS), which is a measure of its rarity and uniqueness.</h3>\n' +      '\t<p>The RS is determined primarily by a Yatโ€™s <b>length</b>. Other factors include the average <b>popularity</b> of the emojis used in the Yat (based on current worldwide usage) and the Yatโ€™s <b>pattern</b> (i.e. repeating emojis or โ€œbookendโ€ emojis).</p>',    features: []  },  price: 400}Order added to cart:  DisplayOrder {  amount_overpaid_in_cents: 0,  created_at: 2021-11-03T22:58:52.245Z,  eligible_for_refund: false,  id: 'd0227ddb-0565-42fd-b242-221f9111889a',  misc_refunded_total_in_cents: 0,  order_items: [..],  order_number: '9111889a',  refunded_total_in_cents: 0,  remaining_due_in_cents: 400,  status: 'Draft',  total_in_cents: 400,  updated_at: 2021-11-03T22:59:02.797Z,  user: DisplayOrderUser {    created_at: 2021-11-03T22:58:51.787Z,    emoji_ids: undefined,    free_limit: 1,    id: '9a1b3fca-8f27-4f96-9596-f1b6a9c9894c',    is_active: undefined,    pubkeys: [      '5a947509205e94deb68151977264edafb9a8579d1240469fd90e0713d8c88217'    ],    remaining_free_emoji: 1,    role: 'User',    updated_at: 2021-11-03T22:58:52.247Z,    alternate_id: null,    email: '[email protected]',    first_name: 'Testy',    last_name: 'McTesty',    source: null,    two_factor_auth: null  },  user_id: '9a1b3fca-8f27-4f96-9596-f1b6a9c9894c',  expires_at: null,  organization_id: null,  paid_at: null,  seconds_until_expiry: null}Apply promo code request succeeded:  DisplayOrder {  amount_overpaid_in_cents: 0,  created_at: 2021-11-03T22:59:07.841Z,  eligible_for_refund: false,  id: '8131c536-acfd-4b24-acd0-8da100a6cb79',  misc_refunded_total_in_cents: 0,  order_items: [..],  order_number: '00a6cb79',  refunded_total_in_cents: 0,  remaining_due_in_cents: 0,  status: 'PendingPayment',  total_in_cents: 0,  updated_at: 2021-11-03T22:59:07.884Z,  user: DisplayOrderUser {    created_at: 2021-11-03T22:58:51.787Z,    emoji_ids: undefined,    free_limit: 1,    id: '9a1b3fca-8f27-4f96-9596-f1b6a9c9894c',    is_active: undefined,    pubkeys: [      '5a947509205e94deb68151977264edafb9a8579d1240469fd90e0713d8c88217'    ],    remaining_free_emoji: 0,    role: 'User',    updated_at: 2021-11-03T22:59:07.845Z,    alternate_id: null,    email: '[email protected]',    first_name: 'Testy',    last_name: 'McTesty',    source: null,    two_factor_auth: null  },  user_id: '9a1b3fca-8f27-4f96-9596-f1b6a9c9894c',  expires_at: 2021-11-03T23:14:02.988Z,  organization_id: null,  paid_at: null,  seconds_until_expiry: 895}Checkout succeeded:  DisplayOrder {  amount_overpaid_in_cents: 0,  created_at: 2021-11-03T22:59:07.841Z,  eligible_for_refund: true,  id: '8131c536-acfd-4b24-acd0-8da100a6cb79',  misc_refunded_total_in_cents: 0,  order_items: [..],  order_number: '00a6cb79',  refunded_total_in_cents: 0,  remaining_due_in_cents: 0,  status: 'Paid',  total_in_cents: 0,  updated_at: 2021-11-03T22:59:08.084Z,  user: DisplayOrderUser {    created_at: 2021-11-03T22:58:51.787Z,    emoji_ids: undefined,    free_limit: 1,    id: '9a1b3fca-8f27-4f96-9596-f1b6a9c9894c',    is_active: undefined,    pubkeys: [      '5a947509205e94deb68151977264edafb9a8579d1240469fd90e0713d8c88217'    ],    remaining_free_emoji: 0,    role: 'User',    updated_at: 2021-11-03T22:59:08.078Z,    alternate_id: null,    email: '[email protected]',    first_name: 'Testy',    last_name: 'McTesty',    source: null,    two_factor_auth: null  },  user_id: '9a1b3fca-8f27-4f96-9596-f1b6a9c9894c',  expires_at: null,  organization_id: null,  paid_at: 2021-11-03T22:59:08.078Z,  seconds_until_expiry: null}URL added to yat.LookupResponse {  stats: [    EmojiStatsResponseMetrics {      description: 'Number of times emoji was looked up via API',      finish_date: 2021-11-03T22:59:14.544Z,      key: '๐Ÿ’ผ๐ŸŒฒ๐Ÿ”ฉ๐Ÿ’',      metric: 'api_emoji_lookups',      start_date: 2021-10-06T22:59:14.544Z,      value: 0    }  ],  error: undefined,  result: [    EidResponseResult {      data: 'http://api-docs.y.at/docs/sdks/nodejs/sdk_nodejs_index',      hash: 'cdc56f98660c2d684605ada33266918043d7d1935e2b9f13550b32d05191bc7a',      tag: '0x4001'    }  ],  status: true}Bye!

Registering a new account#

There are a few ways to register a new account. The majority of use cases will make use of y.at's custodial wallet, in which case, you just need an alternate_id, source, and password to create a new account. You can also optionally provide some personal details to personalise your profile, such as first and last name.

It is possible to register without supplying a password. If you register with an email only, then the initial login will require you to click a magic link emailed to you to complete registration.

More details on user registration is provided in the Creating a new user section.

async function register() {    let details = yat.RegisterUserParameters.constructFromObject({        first_name: "Testy",        last_name: "McTesty",        source: 'yat-docs',        alternate_id,        password,    });    try {        await api.users().createUser(details);
        console.log(`Registered user`);        return true;    } catch (err) {        const alreadyRegistered = err.status === 422 && err.body.fields.alternate_id[0].code === "uniqueness";        if (!alreadyRegistered) {            console.log(`Could not register an account: ${err.error}`);        } else {            console.log(`User was already registered, continuing`);        }        return alreadyRegistered;    }}
async function login() {    try {        await api.login(alternate_id, password);        let res = await api.users().getAccount();        console.log(res);    } catch (res) {        console.log(`Could not log in: ${res.error}`);        throw new Error("Could not login");    }}
register().then(login).then((res) => {    console.log("Bye!")});

If the registration request is successful an access token and refresh token are returned. The getAccount logic referenced by the documentation can then be used to get the user record:

{    "user": {        "id": "bbfaad2c-4478-4387-a569-e93f979a7817",        "email": null,        "alternate_id": "alternate-id-provided",        "first_name": "Testy",        "last_name": "McTesty",        "role": "User",        "two_factor_auth": null,        "free_limit": 1,        "remaining_free_emoji": 1,        "source": "yat-docs",        "created_at": "2020-09-22T21:26:51.545933Z",        "updated_at": "2020-09-25T11:17:31.224291Z"    },    "role": "User",    "global_scopes": [        "cart:show",        "cart:update",        "user:createApiKey",        "edition:read",        "emoji::transfer",        "lootbox:use",        "order:readSelf",        "organizationList:read",        "nftSignature:write",        "paymentMethod:destroy",        "paymentMethod:read",        "paymentMethod:setDefault",        "userData:update",        "user:deleteSelf",        "userInterest:delete",        "userInterest:read",        "userInterest:write",        "user:writeSelf"    ],    "organization_roles": {},    "organization_scopes": {},    "pending_transfers": [],    "pubkeys": [        "d87a65697bfb7b9ffe19007753a7eacf77fec982b2484cc36659959d90d29131"    ]}

User Authentication#

Every user has a pre-defined role which includes exactly what endpoints they may access.

Any requests that change or access user specific data require an Authorization header that includes Bearer {access_token}. The access token expires after a set amount of time, after which a call to the /auth/token/refresh endpoint with the refresh_token will return a new access_token for the next set time period.

The Yat SDKs manage the access token state on your behalf, and automatically submit the refresh token if your current access token is about to expire, so if you're using the SDKs, you don't have to worry about any of this beyond keeping a reference to the api instance after using the appropriate login function:

const yat = require("yatJs");const api = new yat.YatJs();
// ...
async function login() {    try {        let res = await api.login(email, password);        // Api calls that require auth are automatically managed for you...        let result = await api.someApi().someAuthFunction(foo);    } catch (err) {        console.log(`Could not log in: ${err.error}`);        throw new Error("Could not login");    }}
// ...
func login(completion: @escaping (Result<TokenResponse, Error>) -> Void) {    let details = LoginRequest(password: password, email: email)    UserAuthenticationAPI.login(body: details) { (result) in         switch result {        case .success(let token):            YatSDKAPI.yatCredential = YatCredentials(accessToken: token.accessToken, refreshToken: token.refreshToken)            completion(.success(token))        case .failure(let error):            completion(.failure(error))        }    }}

Purchasing a Yat#

y.at supports purchasing a Yat using credit card and cryptocurrencies. All Yat purchases can be discounted with a Promo Code, and if the discount is 100%, then no payment details are required whatsoever.

The demonstration flow above illustrates how to go about claiming a yat by applying a Promo Code with 100% discount. We'll describe that flow first, and then talk about how the credit card and crypto payments work.

Checking Yat availability#

Before you can add a yat to your cart, you should check whether it is available. This is done using the search function in the Cart API.

async function checkYatAvailability(myYat) {    console.log(`Checking ${myYat} availability...`);    try {        const yatInfo = await api.emojiID().searchEmojiID(myYat);        console.log(yatInfo.result);        if (!yatInfo.result.available) {            console.log(`Bad luck :(, ${yat} is not available.`);        }    } catch (res) {        console.log(`Could not check availability: ${res.error}`);        throw new Error("Could not check availability");    }}

A typical successful response to the search function returns the details of the Yat:

{   "result":{      "emoji_id":"๐Ÿ’Ž๐Ÿ’Ž๐Ÿ’Ž",      "available":true,      "availability":"Available",      "price":340000,      "rhythm_score":99,      "length":3,      "copy":{         "description":"<h3>Every Yat is one-of-a-kind, and is pay once, own forever. The price is based on its Rhythm Score (RS), which is a measure of its rarity and uniqueness.</h3>\n\t<p>The RS is determined primarily by a Yatโ€™s <b>length</b>. Other factors include the average <b>popularity</b> of the emojis used in the Yat (based on current worldwide usage) and the Yatโ€™s <b>pattern</b> (i.e. repeating emojis or โ€œbookendโ€ emojis).</p>",         "features":[            "With a <b>RS of 99</b>, this Yat is of epic rarity. <b>Only 1 in 15 trillion Yats</b> achieve this incredible score. Owning this Yat is as special as owning your very own tropical island, or perhaps all of Hawaii.",            "This Yat is <b>3 emojis in length</b> making it very easy to remember, and wonderful for storytelling. It's the perfect Yat.",            "The average popularity of the emojis in your Yat is <b>10.0 out of 10</b>; they are the most beloved emojis on our planet..",            "The <b>pattern</b> score for this Yat is <b>8.6</b>. Your Yat has all repeating emojis, making it a truly exceptional Yat."         ]      },      "stats":[         {            "metric":"api_emoji_lookups",            "description":"Number of times emoji was looked up via API",            "start_date":"2021-10-07T14:49:16.976726Z",            "finish_date":"2021-11-04T14:49:16.976735Z",            "key":"๐Ÿ’Ž๐Ÿ’Ž๐Ÿ’Ž",            "value":0         }      ],      "generation":1,      "short_names":[         "diamond",         "diamond",         "diamond"      ],      "minted":false,      "flippable_emoji":[         true,         true,         true      ],      "shape":{         "shape":"Bookends",         "pattern":{            "match_indexes":[                           ],            "original":"๐Ÿ’Ž๐Ÿ’Ž๐Ÿ’Ž",            "value":"abcdea"         }      }   },   "alternates":[   ]}

Claiming a Yat with a promo code#

You can claim a Yat without providing any payment details by making use of the Free payment method. This method requires a promo code(s) to be applied to the order since it will only succeed if the total cost of the order is zero.

The recommended claim process is:

  • Clear the cart
  • Add an item to the cart with the relevant Promo code
  • Checkout with the Stripe payment provider to put your cart into a PendingPayment state (returns a payment intent on the order that can be ignored).
  • Apply a 100% discount code to the PendingPayment order via the applyPromoCode method
  • Checkout with the Free payment provider.

This flow is achieved with three consecutive calls to the API:

async function claimYat(myYat) {    // Clear the cart    await api.cart().clearCart();        // Add the yat to the cart. This time use the constructor    const order = new yat.AddItemsCartRequest([        {            emoji_id: myYat,        }    ]);    const cart = await api.cart().addItems(order);    console.log("Order added to cart: ", cart);
    // Checkout, currently to add a promo method the cart must be in a pending payment state    await api.cart().checkout({ method: "Stripe"});        let promoCodeResult = await api.cart().applyPromoCode({        'code': "FREEYAT" // String | Redemption code    });    console.log("Apply promo code request succeeded: ", promoCodeResult);
    let checkoutFreeResult = await api.cart().checkout({ method: "Free" });    console.log("Checkout succeeded: ", checkoutFreeResult);    await new Promise(r => setTimeout(r, 5000));
    return myYat;}

The final response from a successful checkout is the final order object and looks similar to the following:

{  "created_at": "2019-08-24T14:15:22Z",  "eligible_for_refund": true,  "expires_at": "2019-08-24T14:15:22Z",  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "misc_refunded_total_in_cents": 0,  "order_items": [    {      "client_fee_in_cents": 0,      "code_id": "c6a02b7d-40f4-4982-9c97-607f4e20761a",      "company_fee_in_cents": 0,      "created_at": "2019-08-24T14:15:22Z",      "emoji_id": "๐Ÿ‘โค๏ธ๐Ÿ€",      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "item_type": "Discount",      "order_id": "93101167-9065-4b9c-b98b-5d789a3ed9fe",      "parent_id": "1c6ca187-e61f-4301-8dcb-0e9749e89eef",      "quantity": 0,      "refunded_quantity": 0,      "unit_price_in_cents": 0,      "updated_at": "2019-08-24T14:15:22Z"    }  ],  "order_number": "string",  "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",  "paid_at": "2019-08-24T14:15:22Z",  "payment_method_data": {},  "refunded_total_in_cents": 0,  "seconds_until_expiry": 0,  "status": "Cancelled",  "total_in_cents": 0,  "updated_at": "2019-08-24T14:15:22Z",  "user": {    "alternate_id": "string",    "created_at": "2019-08-24T14:15:22Z",    "deactivated_at": "2019-08-24T14:15:22Z",    "email": "string",    "first_name": "string",    "free_limit": 0,    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",    "is_active": true,    "last_name": "string",    "remaining_free_emoji": 0,    "role": "Admin",    "source": "string",    "two_factor_auth": null,    "updated_at": "2019-08-24T14:15:22Z"  },  "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"}

Yat purchases using a Credit card#

๐Ÿšง COMING SOON ๐Ÿšง

Yat purchases using cryptocurrency#

๐Ÿšง COMING SOON ๐Ÿšง

Fetching a list of yats you own#

Congratulations! You've registered an account, and have claimed or purchased a shiny new yat. Let's make sure the sale went through.

Assuming you have an api instance and are logged in, fetching a list of yats that you own is very simple using the emojiID API.

const yat = require("yatJs");const api = new yat.YatJs();
// Log in ...
async function fetchMyYats() {    let yats = await api.emojiID().listEmojiIDs();    return yats;}

The method very simply returns an array of Yats owned by the logged-in user:

[    "๐ŸŽ€๏ธโ™๐Ÿ‹",    "๐Ÿ‘โค๏ธ๐Ÿ€"]

Adding an emoji id record to the yat#

Let's start making those yats useful!

It's possible to associate all kinds of data with your Yat.

beta

Whilst the API does support all these data types, only a subset of power-ups such as URL redirects are supported on the y.at platform presently, so this guide will focus on that use case.

Once again, the emojiID API provides the tools you need to add emoji id records to your yat. The edit function allows you to insert and delete records from the emoji id record. Because of how emoji id records will be represented on the blockchain, updates to records are not possible. However, you can simulate an update by deleting the record and then inserting a new one with the updated data.

info

Note: some category types, such as payment addresses, only allow for one record by default so when these encounter new records they are replaced. A parameter to the edit endpoint bypass_single_restrictions set to true allows this behavior to be suppressed. Please refer to the Yat record categories for detailed information on record categories.

Deleting records requires you to know the hash of the data you're deleting. You can calculate this yourself, using a 256-byte Blake2b digest, or simply copy the hash from the result of the lookup API function.

The edit endpoint returns the list of yats that were modified as a result of the query, which you can typically ignore.

async function addYatRecord(yat, url) {    try {        // Delete existing record        let updates = { delete: ["3a1bb6cdff154b978e7b1bf294a1e74925b3db80953d0c308a293da93777fd35"] };        await api.emojiID().editEmojiID(yat, updates);        updates = { insert: [{ tag: "0x4001", data: url }] };        await api.emojiID().editEmojiID(yat, updates);        console.log("URL added to yat.");    } catch (err) {        console.log("Error Result of adding record request: ", err.body);    }    return yat;}

Fetching the data associated with a yat#

The lookup function in the EmojiID API is likely the most important from a client-side perspective. It is an unauthenticated endpoint, so you don't need an SDK instance to use it, but it is provided for completeness and convenience.

The lookup function takes two parameters: the yat to look up, and array of category tags to filter the results. If you omit the tag filter, then the response contains all emoji id records attached to the yat.

const yat = require("yatJs");const api = new yat.YatJs();
// ...
async function printYatRecords(yat) {    try {        let records = await api.emojiID().lookupEmojiID(yat);        console.log(records);    } catch (err) {        console.log("Error fetching yat data: ", err.body)    }}

A typical response to a lookup request contains an error object (null on a successful lookup), a response status result (set to true), a counter, views_past_month indicating how many times the yat has been requested in the last month, and an array of emoji id records.

Each record contains the data itself, the category tag for the record, and the hash of the data.

A typical response looks like

{    "status": true,    "result": [        {            "tag": "0x4001",            "data": "http://api-docs.y.at/docs/",            "hash": "cdc56f98660c2d684605ada33266918043d7d1935e2b9f13550b32d05191bc7a"        }    ],    "error": null,    "views_past_month": 19}

Conclusion#

And there you have it. Basic Yat integration from start to finish. From here you may wish to dive deeper into the SDK documentation, or refer to the API reference material, or take your integration knowledge even further with one of our advanced integration topics.