The Two Tap Developer Hub

Welcome to the Two Tap developer hub. You'll find comprehensive guides and documentation to help you start working with Two Tap as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started    

Search results for "{{ search.query }}"

No results found for "{{search.query}}". 
View All Results
 
gethttps://api.twotap.com/v1.0/product
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

site_id = 'A_SITE_ID'
product_md5 = 'A_PRODUCT_MD5'
destination_country = 'Japan'

response = RestClient.get "https://api.twotap.com/v1.0/product?public_token=PUBLIC_TOKEN&site_id=#{site_id}&product_md5=#{product_md5}&destination_country=#{destination_country}"

puts response.body
#!/bin/bash

PUBLIC_TOKEN=PUBLIC_TOKEN
SITE_ID=SITE_ID
PRODUCT_MD5=PRODUCT_MD5

curl https://api.twotap.com/v1.0/product?public_token=$PUBLIC_TOKEN\&site_id=$SITE_ID\&product_md5=$PRODUCT_MD5
#!/usr/bin/python

import requests

public_token = 'PUBLIC_TOKEN'
site_id = 'SITE_ID'
product_md5 = 'PRODUCT_MD5'

r = requests.get('https://api.twotap.com/v1.0/product?public_token=' + public_token + '&site_id=' + site_id + '&product_md5=' + product_md5)
print(r.json())
var public_token = 'PUBLIC_TOKEN';
var site_id = 'SITE_ID';
var product_md5 = 'PRODUCT_MD5';

$.get('https://api.twotap.com/v1.0/product?public_token=' + public_token + '&site_id=' + site_id + '&product_md5=' + product_md5, function(data, status) {
  console.log(data);
})
var request = require("request");

var public_token = 'PUBLIC_TOKEN';
var site_id = 'SITE_ID';
var product_md5 = 'PRODUCT_MD5';

request({
  url: 'https://api.twotap.com/v1.0/purchase/status?public_token=' + public_token + '&site_id=' + site_id + '&product_md5=' + product_md5,
  method: "GET"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$product = $api->product()->get('SITE_ID', 'PRODUCT_MD5');
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    site_id := "SITE_ID"
    product_md5 := "PRODUCT_MD5"
    url := "https://api.twotap.com/v1.0/purchase/status?public_token=" + public_token + "&site_id=" + site_id + "&product_md5=" + product_md5
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var site_id = "SITE_ID";
      var product_md5 = "PRODUCT_MD5";
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/purchase/status?public_token=" + public_token + "&site_id=" + site_id + "&product_md5=" + product_md5);
      var result = client.GetAsync ("").Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{
  "message": "done",
  "product": {
    "url": "http://www.stevemadden.com/product/BENJAMIN/238229.uts",
    "title": "BENJAMIN",
    "price": "6202 JPY",
    "original_price": "6402 JPY",
    "description": "<p> Channel your inner tomboy wearing the BENJAMIN brogued oxfords. Comfortable and dapper, they can be dressed up or down. Style with fuzzy wool tights and a plaid skirt for a heritage look.</p>\n<ul> <li> Man-made upper material</li> <li> Man-made lining</li> <li> Man-made sole</li> <li> 1 inch heel height</li>\n</ul>\n<p> </p>",
    "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK%3F%24MR%252DZOOM%24%26id%3DgE8rO1%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
    "alt_images": [
      "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK%3F%24MR-ZOOM%24",
      "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_FRONT%3F%24MR-ZOOM%24",
      "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_BACK%3F%24MR-ZOOM%24",
      "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_SIDE%3F%24MR-ZOOM%24",
      "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_TOP%3F%24MR-ZOOM%24"
    ],
    "categories": [
      "Apparel & Accessories~~Shoes~~Flats"
    ],
    "category_attributes": [
      "g-women"
    ],
    "site_categories": [
      "Flats"
    ],
    "weight": "1000",
    "required_field_values": {
      "color": [
        {
          "alt_images": [
            "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK%3F%24MR-ZOOM%24",
            "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_FRONT%3F%24MR-ZOOM%24",
            "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_BACK%3F%24MR-ZOOM%24",
            "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_SIDE%3F%24MR-ZOOM%24",
            "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_TOP%3F%24MR-ZOOM%24"
          ],
          "dep": {
            "size": [
              {
                "dep": {},
                "extra_info": "",
                "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK%3F%24MR%252DZOOM%24%26id%3DgE8rO1%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                "price": "6202 JPY",
                "text": "6",
                "value": "6",
                "weight": ""
              }
            ]
          },
          "extra_info": "",
          "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK%3F%24MR%252DZOOM%24%26id%3DgE8rO1%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
          "price": "6202 JPY",
          "text": "BLACK",
          "value": "http://s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-TAILORED_BENJAMIN_BLACK_SWATCH?hei=22&qlt=85,1&wid=22&fmt=jpeg&resMode=bicub&op_sharpen=1",
          "weight": ""
        }
      ]
    },
    "required_field_names": [
      "color",
      "size",
      "quantity"
    ],
    "updated_at": "2016-10-11T06:10:32.468Z",
    "source": "cache",
    "site_id": "51cd7d0f55a0f902d8000003",
    "md5": "ca346534f5f8b840b128e0c2047d7750"
  }
}

Body Params

public_token
string
required

Your Two Tap public token.

site_id
string
required

The site_id of the product you are looking for.

product_md5
string
required

The product_md5 of the product you are looking for.

product_attributes_format
string

(Optional) If you set this to flat the product attributes will be returned as an array instead of a tree.

destination_country
string

(Optional) If you know most orders are coming from Japan, for instance, you can set this as your default country to receive prices in local currency.

in_catalog
boolean

(Optional) Default false. Fail if the product is NOT present in the Catalog.

 

Given a site_id and a product_md5 this endpoint will return the latest information Two Tap has about it.

By default this queries Two Tap's internal DB that contains all products crawled via the catalog and /cart. This means it might return information about products that are not in /product/search or /product/scroll. Set in_catalog to true to only return products from the Catalog.

To find product_md5s use /product/search or /product/scroll.

In production, please cache responses for at least 5 minutes.

Response description

{
  "message": "String. If Two Tap has found the product. eg: 'done', 'failed'",
  "product": {
    "url": "String. URL of the product.",
    "title": "String. The product's title.",
    "brand": "String. Where available the product's brand will be returned. eg: 'Adidas', 'Vans'",
    "price": "String/Price. The product's price.",
    "original_price": "String/Price. The product's original price.",
    "description": "String/HTML. The product description.",
    "image": "String/URL. The product's image URL.",
    "alt_images": "Array. Contains the product's secondary images.",
    "extra_info": "String. Other useful information regarding the product. e.g.: 'Last one available.'",
    "categories": "Array. Contains the product categories.",
    "category_attributes": "Array. Contains special category attributes. eg: ['g-women', 'g-boys', 'g-girls']",
    "site_categories": "Array. Contains the categories on the retailer's site. eg: ['Sale', 'Womens']",
    "weight": "Integer. The product's weight in grams.",
    "required_field_values": {
      "[attribute_title eg:'color']": [
        {
          "dep": {
            "[dependency_attribute_title eg:'size']": [
              {
                "dep": {},
                "extra_info": "String. Other useful information regarding the product. e.g.: 'Last one available.'",
                "image": "String/URL. The product's image URL",
                "price": "String/Price. The attribute's price.",
                "text": "String/Text or URL. Either a string with the attribute name or URL to thumbnail (if color).",
                "value": "String/Text or URL. Either a string with the attribute value or URL to thumbnail (if color).",
                "weight": "Integer. Product weight in grams."
              },
            ]
          },
          "extra_info": "..",
          "alt_images": "..",
          "image": "..",
          "price": "..",
          "text": "..",
          "value": "..",
          "weight": ".."
        }
      ]
    },
    "required_field_names": "Array. Contains the required field names. In the example detailed above this would be ['color', 'size', 'quantity'].",
    "required_fields": {
      "[field_title eg:'color']": {
        "data": [
          {
            "input_name": "String. Type of input. eg: 'SELECT', 'INPUT",
            "input_type": "String. Type of choice. eg: 'select-one', 'text'"
          }
        ]
      },
    },
    "updated_at": "Integer. Timestamp with the last update.",
    "site_id": "String. The product's site_id.",
    "md5": "String. The product's md5."
  }
}
Suggest Edits

/product/scroll

 
posthttps://api.twotap.com/v1.0/product/scroll
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

filter = {}
size = 10
destination_country = 'Japan'

response = RestClient.post "https://api.twotap.com/v1.0/product/scroll?public_token=PUBLIC_TOKEN", { 
  filter: filter,
  size: size,
  destination_country: destination_country
}

puts response.body
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

scroll_id = 'INITIAL_BATCH_SCROLL_ID'
size = 10
destination_country = 'Japan'

response = RestClient.post "https://api.twotap.com/v1.0/product/scroll?public_token=PUBLIC_TOKEN", { 
  scroll_id: scroll_id,
  size: size,
  destination_country: destination_country
}

puts response.body
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$filter = [];
$size = 10;

$response = $api->product()->scroll($filter, $size);
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$filter = null;
$size = 10;
$scrollId = 'INITIAL_BATCH_SCROLL_ID';

$response = $api->product()->scroll($filter, $size, $scrollId);
#!/usr/bin/python

import requests, json

public_token = 'YOUR_PUBLIC_TOKEN'

payload = {
  'filter': {
    'brands': [ 'adidas' ]
  }
}

r = requests.post('https://api.twotap.com/v1.0/product/search?public_token=' + public_token, data=json.dumps(payload), headers={'Content-Type': 'application/json'})

print(r.json())
#!/usr/bin/python

import requests, json

public_token = 'YOUR_PUBLIC_TOKEN'

payload = {
  'scroll_id': 'INITIAL_BATCH_SCROLL_ID'
}

r = requests.post('https://api.twotap.com/v1.0/product/scroll?public_token=' + public_token, data=json.dumps(payload), headers={'Content-Type': 'application/json'})

print(r.json())
A binary file was returned

You couldn't be authenticated

{
  "message": "done",
  "scroll_id": "a long id",
  "total": 324216,
  "size": 10,
  "products": [
    {
      "url": "http://www.stevemadden.com/product/LIZLEE/238315.uts",
      "title": "LIZLEE",
      "description": "<p> The pointy toe on the LIZLEE is a modern twist on the classic Mary Jane. These adorable flats will add charm to a girly matching set.</p>\n<ul> <li> Leather upper material</li> <li> Man-made lining</li> <li> Man-made sole</li> <li> .25 inch heel height</li>\n</ul>",
      "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
      "price": "4137 JPY",
      "original_price": "7236 JPY",
      "categories": [
        "Everything Else"
      ],
      "site_categories": [
        "Women's Clearance",
        "Flats"
      ],
      "required_field_names": [
        "color",
        "size",
        "quantity"
      ],
      "required_field_values": {
        "color": [
          {
            "alt_images": [
              "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR-ZOOM%24",
              "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER%3F%24MR-ZOOM%24",
              "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_BACK%3F%24MR-ZOOM%24",
              "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_FRONT%3F%24MR-ZOOM%24",
              "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_TOP%3F%24MR-ZOOM%24"
            ],
            "dep": {
              "size": [
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "5",
                  "value": "5",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "5.5",
                  "value": "5.5",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "6",
                  "value": "6",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "6.5",
                  "value": "6.5",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "7",
                  "value": "7",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "7.5",
                  "value": "7.5",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "8",
                  "value": "8",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "8.5",
                  "value": "8.5",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "9",
                  "value": "9",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "9.5",
                  "value": "9.5",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "10",
                  "value": "10",
                  "weight": ""
                },
                {
                  "dep": {},
                  "extra_info": "",
                  "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
                  "price": "4137 JPY",
                  "text": "11",
                  "value": "11",
                  "weight": ""
                }
              ]
            },
            "extra_info": "",
            "image": "https://px.twotap.com/unsafe/http%3A//s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SIDE%3F%24MR%252DZOOM%24%26id%3DXDCr51%26rgn%3D-110%2C0%2C3222%2C3000%26scl%3D7.159904534606206",
            "price": "4137 JPY",
            "text": "BLACK LEATHER",
            "value": "http://s7d9.scene7.com/is/image/SteveMadden/STEVEMADDEN-FLATS_LIZLEE_BLACK-LEATHER_SWATCH?hei=22&qlt=85,1&wid=22&fmt=jpeg&resMode=bicub&op_sharpen=1",
            "weight": ""
          }
        ]
      },
      "site_id": "51cd7d0f55a0f902d8000003",
      "all_categories": [
        "Everything Else"
      ],
      "deep_categories": [
        "Everything Else"
      ],
      "md5": "1ff00de9791626a9a068eb235dd733f2"
    }
    ....
  ]
}

Body Params

public_token
string
required

Your Two Tap public token.

filter
string

(Optional) Allows filtering of the products.

size
string

(Optional) How many items to send in one batch.

scroll_id
string

(Optional) Use scroll_id to get the next batch of items.

product_attributes_format
string

(Optional) If you set this to flat the product attributes will be returned as an array instead of a tree.

destination_country
string

(Optional) If you know most orders are coming from Japan, for instance, you can set this as your default country to receive prices in local currency.

 

This endpoint allows you to download Two Tap's product catalog.

The first /product/scroll request will return a scroll_id that you can use to get the next batch of products. See the example snippets for details.

The scroll_id is valid for 30 seconds. As you're downloading the product in batches you have to hit the API every 30 seconds or it will expire.

If you are looking for real-time consumer queries please see /product/search.

In production, please cache responses for at least 5 minutes.

The 'filter' parameter

The filter parameter is a hash that looks like this:

{
  "keywords": "String. eg white dresses or sneakers.",
  "keywords_fields": [ "Array. Works together with keywords. Choose the fields  in which search/scroll should look. Possible values are 'title', 'brand', 'description', 'url'." ],  
  "site_ids": [ "Array. Possible site_ids"],
  "genders": [ "Array. Possible genders (g-men, g-women, g-boys, g-girls)." ],
  "categories": [ "Array. Possible categories (see /product/taxonomy)." ],
  "price_ranges": [ { "from": 0, "to": 500, "currency": "JPY" } ],
  "brands": [ " Array. Possible brands." ],
  "intl_enabled": "Boolean. If the site supports international orders."
}

Response description

{
  "message": "String. If Two Tap has completed the request. eg: 'done', 'failed'",
  "total": "Integer. The total number of items found.",
  "size": "Integer. The size of the query.",
  "scroll_id": "String. A very long id.",
  "products": [
    {
      "url": "String. URL of the product.",
      "title": "String. The product's title.",
      "brand": "String. Where available the product's brand will be returned. eg: 'Adidas', 'Vans'",
      "price": "String/Price. The product's price.",
      "original_price": "String/Price. The product's original price.",
      "description": "String/HTML. The product description.",
      "image": "String/URL. The product's image URL.",
      "categories": "Array. Contains the product categories.",
      "category_attributes": "Array. Contains special category attributes. eg: ['g-women', 'g-boys', 'g-girls']",
      "site_categories": "Array. Contains the categories on the retailer's site. eg: ['Sale', 'Womens']",
      "required_field_values": {
        "[attribute_title eg:'color']": [
          {
            "dep": {
              "[dependency_attribute_title eg:'size']": [
                {
                  "dep": {},
                  "extra_info": "String. Other useful information regarding the product. e.g.: 'Last one available.'",
                  "image": "String/URL. The product's image URL",
                  "price": "String/Price. The attribute's price.",
                  "text": "String/Text or URL. Either a string with the attribute name or URL to thumbnail (if color).",
                  "value": "String/Text or URL. Either a string with the attribute value or URL to thumbnail (if color).",
                  "weight": "Integer. Product weight in grams."
                },
              ]
            },
            "extra_info": "..",
            "alt_images": "..",
            "image": "..",
            "price": "..",
            "text": "..",
            "value": "..",
            "weight": ".."
          }
        ]
      },
      "required_field_names": "Array. Contains the required field names. In the example detailed above this would be ['color', 'size', 'quantity'].",
      "required_fields": {
        "[field_title eg:'color']": {
          "data": [
            {
              "input_name": "String. Type of input. eg: 'SELECT', 'INPUT",
              "input_type": "String. Type of choice. eg: 'select-one', 'text'"
            }
          ]
        },
      },
      "updated_at": "Integer. Timestamp with the last update.",
      "site_id": "String. The product's site_id.",
      "md5": "String. The product's md5."
    }
  ]
}
Suggest Edits

/product/filters

 
posthttps://api.twotap.com/v1.0/product/filters
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

filter = {}

response = RestClient.post "https://api.twotap.com/v1.0/product/filters?public_token=PUBLIC_TOKEN", { 
  filter: filter
}

puts response.body
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$filter = [];

$filters = $api->product()->filters($filter);
#!/usr/bin/python

import requests, json

public_token = 'YOUR_PUBLIC_TOKEN'

payload = {
  'filter': {}
}

r = requests.post('https://api.twotap.com/v1.0/product/filters?public_token=' + public_token, data=json.dumps(payload), headers={'Content-Type': 'application/json'})

print(r.json())
A binary file was returned

You couldn't be authenticated

{
  "message": "done",
  "categories": [
    {
      "name": "Apparel & Accessories",
      "count": 116965
    },
    {
      "name": "Shirts & Shorts",
      "count": 103224
    },
    {
      "name": "Apparel & Accessories~~Clothing",
      "count": 96820
    }
    ...
  ],
  "genders": [
    {
      "name": "g-women",
      "count": 114125
    },
    {
      "name": "g-men",
      "count": 44244
    },
    {
      "name": "g-girls",
      "count": 33128
    },
    {
      "name": "g-boys",
      "count": 17251
    }
  ],
  "site_ids": [
    {
      "name": "54f96acd69702d0f9e110000",
      "count": 46440
    },
  ],
  "brands": [
    {
      "name": "Ice",
      "count": 39493
    },
    {
      "name": "Forever 21",
      "count": 28723
    }
    ....
  ]
}

Body Params

public_token
string
required

Your Two Tap public token.

filter
string

(Optional) Allows filtering of the products.

 

Use this endpoint to get a list of possible filters for certain search criteria.

For instance, if the shopper is looking at products from Kohls, you can display a set of filters (eg. categories, genders) that are available just for that site_id.

The filters variable is the same as in /purchase/search.

In production, please cache responses for at least 5 minutes.

Response description

{
  "message": "String. If Two Tap has completed the request. eg: 'done', 'failed'",
  "categories": [
    {
      "name": "String. The category name.",
      "count": "Integer. The number of products for the repective category."
    },
  ],
  "genders": [
    {
      "name": "String. The gender's name. eg: 'g-women', 'g-men', 'g-boys', 'g-girls'",
      "count": "Integer. The number of products for the respective gender for the search criteria."
    },
  ],
  "sizes": [
    {
      "name": "String. The size's name. eg: 'L', 'XXL', '13', 'IT 40', 'UK 4', 'CN 42'",
      "count": "Integer. The number of products for the respective size for the search criteria."
    },
  ],
  "site_ids": [
    {
      "name": "String. The site id.",
      "count": "Integer. The number of products for the respective site for the search criteria."
    },
  ],
  "promotions": [
    {
      "name": "String. The promotion's name. eg: 'p-sale'",
      "count": "Integer. The number of products for the respective promotion for the search criteria."
    },
  ],
  "brands": [
    {
      "name": "String. The brand's name. eg: 'Ice'",
      "count": "Integer. The number of products for the respective brand for the search criteria."
    },
  ],
}
Suggest Edits

/product/taxonomy

 
gethttps://api.twotap.com/v1.0/product/taxonomy
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

response = RestClient.get "https://api.twotap.com/v1.0/product/taxonomy"

puts response.body
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$taxonomy = $api->product()->taxonomy();
#!/usr/bin/python

import requests, json

public_token = 'YOUR_PUBLIC_TOKEN'

r = requests.get('https://api.twotap.com/v1.0/product/taxonomy?public_token=' + public_token, headers={'Content-Type': 'application/json'})

print(r.json())
A binary file was returned

You couldn't be authenticated

{
  "message": "done",
  "taxonomy": [
    "Apparel & Accessories",
    "Apparel & Accessories~~Clothing",
    "Apparel & Accessories~~Clothing~~Activewear",
    "Apparel & Accessories~~Clothing~~Activewear~~Bicycle Activewear",
	  ....
    "Everything Else"
  ]
}
 

This endpoint will return Two Tap's current product taxonomy. All products will belong to one of these categories.

Keep in mind that taxonomy can be updated, and is not final.

Response description

{
  "message": "String. If Two Tap has completed the request. eg: 'done', 'failed'",
  "taxonomy": "Array. Two Tap's current taxonomy. eg: 'Apparel & Accessories', 'Apparel & Accessories~~Clothing','Arts & Entertainment~~Hobbies & Creative Arts~~Collectibles~~Sports Collectibles~~Autographed Sports Paraphernalia~~Baseball & Softball Autographed Paraphernalia'"
}
posthttps://api.twotap.com/v1.0/cart
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

products = [ 
  'http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605/#fae3AT5i',
  'http://www.target.com/p/out-of-stock-product#aiQu2iLo' 
]

response = RestClient.post "https://api.twotap.com/v1.0/cart?public_token=PUBLIC_TOKEN", { products: products }

puts response.body
#!/bin/bash

curl https://api.twotap.com/v1.0/cart?public_token=$PUBLIC_TOKEN --header 'Content-Type: application/json' --data-binary '{
  "products": ["http://fab.com/sale/4850/product/11263/", "http://fab.com/sale/4800/product/43565/"]
}'
#!/usr/bin/python

import requests
import json

PUBLIC_TOKEN = 'YOUR PUBLIC TOKEN'

payload = {'products': [ 'http://fab.com/sale/4850/product/11263/', 'http://fab.com/sale/4800/product/43565/' ]}

response = requests.post('https://api.twotap.com/v1.0/cart?public_token=' + PUBLIC_TOKEN,
        data=json.dumps(payload), headers={'Content-Type': 'application/json'})

print(response.json())
$.post('https://api.twotap.com/v1.0/cart?public_token=PUBLIC_TOKEN', { 
    "products": [ "http://fab.com/sale/4850/product/11263/", "http://fab.com/sale/4800/product/43565/" ]
  }, function(data, status) {
    console.log(data);
});
var request = require("request");
var public_token = 'PUBLIC_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/cart?public_token=' + public_token,
  json: { "products": [ "http://fab.com/sale/4850/product/11263/", "http://fab.com/sale/4800/product/43565/" ]},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php 

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$products = ['http://fab.com/sale/4850/product/11263/', 'http://fab.com/sale/4800/product/43565/'];

$status = $api->cart()->create($products);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    url := "https://api.twotap.com/v1.0/cart?public_token=" + public_token
    var jsonStr = []byte(`{"products":"http://fab.com/sale/4850/product/11263/,http://fab.com/sale/4800/product/43565/"}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/cart?public_token=" + public_token);
      dynamic payload = new ExpandoObject ();
      payload.products = new String[] { "http://fab.com/sale/4850/product/11263/", "http://fab.com/sale/4800/product/43565/" };
      string json = JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json, Encoding.UTF8, "application/json")).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{
  "cart_id": "50f414b9e6a4869bf6000009",
  "message": "still_processing",
  "description": "Still processing."
}                    

Body Params

public_token
string
required

Your Two Tap public token.

products
array of strings
required

A list of product URLs. Max 100 in one request, the API will silently drop anything above that.

finished_url
string

(Optional) An endpoint where Two Tap can POST the product information once the information is retrieved.

finished_product_attributes_format
string

(Optional) If you set this to flat the product attributes will be returned as an array instead of a tree. See /cart/status for more information.

notes
object

(Optional) You can send any information here and it will be attached to the cart and piped back with /status responses and callbacks.

test_mode
string

(Optional) A way to test the API the interface without making actual purchases.

cache_time
int32

(Optional) This controls how fresh you'd like Two Tap's product data to be. Please consult the Product Availability section for more information. In seconds.

destination_country
string

(Optional) If you know most orders are coming from Japan, for instance, you can set this as your default country to receive prices in local currency.

 

Send /cart a list of products and it will return the real-time product information (title, price, image, etc), stock availability, and the required fields that the user has to fill out to finalize the purchase.

To ease development it's recommended you append a random hash value at the end of each URL. For instance, http://www.target.com/product#UNIQUE_ID. At /cart/status, Two Tap will return structured information about each product and you'll be able to match what the URL you sent with the original_url argument from the response. This is also helpful if you want to purchase the same product with different attributes. When calling /purchase just match the #UNIQUE_ID with original_url and product_md5.

You can not add products to an existing cart. If you'd like to add more products feel free to start a new /cart call.

This request is processed in the background. You either have to call /cart/status which will retrieve the relevant information once the job is finished, or set finished_url which will trigger a callback to your servers.

Carts expire after two weeks (/cart/status will return not_found).

Ideally, send /cart the actual product URLs (eg http://store.com/url). You can send affiliate links later on with /purchase.

That being said, /cart does accept and follow most affiliate links to try to find the actual product URL underneath. However, this can cause various confusions if you are expecting consistent product MD5s. Ideally, send a #UNIQUE_ID with affiliate links as well.

Response description

{
  "cart_id": "String. The newly generated cart id.",
  "message": "String. If Two Tap has completed the request. Always 'still_processing'",
  "description": "String. A more human-friendly description of the message. eg: 'Cart is processing.'"
}
Suggest Edits

/cart/status

 
gethttps://api.twotap.com/v1.0/cart/status
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

cart_id = ARGV[0]

response = RestClient.get "https://api.twotap.com/v1.0/cart/status?public_token=PUBLIC_TOKEN&cart_id=#{cart_id}"

puts response.body
#!/bin/bash

PUBLIC_TOKEN=PUBLIC_TOKEN
CART_ID=CART_ID

curl https://api.twotap.com/v1.0/cart/status?public_token=$PUBLIC_TOKEN\&cart_id=$CART_ID
#!/usr/bin/python

import requests

public_token = 'PUBLIC_TOKEN'
cart_id = 'CART_ID'
r = requests.get('https://api.twotap.com/v1.0/cart/status?public_token=' + public_token + '&cart_id=' + cart_id)
print(r.json())
$.get("https://api.twotap.com/v1.0/cart/status?public_token=PUBLIC_TOKEN&cart_id=CART_ID", function(data, status) {
  console.log(data);
})
var request = require("request");
var public_token = 'PUBLIC_TOKEN';
var cart_id = 'CART_ID';
request({
  url: 'https://api.twotap.com/v1.0/cart/status?public_token=' + public_token + '&cart_id=' + cart_id,
  method: "GET"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php 

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$cartId = 'CART_ID';

$status = $api->cart()->status($cartId);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    cart_id := "CART_ID"
    url := "https://api.twotap.com/v1.0/cart/status?public_token=" + public_token + "&cart_id=" + cart_id
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var cart_id = "CART_ID";
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/cart/status?public_token=" + public_token + "&cart_id=" + cart_id);
      var result = client.GetAsync ("").Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{
  "sites": {
    "5225da1055a0f96c8b000004": {
      "cart": {
        "e987cc0e00b50b6edb0b785ca0ce3efb": {
          "last_message": "Fetching title.",
          "percent_done": 33.53819139596137,
					"url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
          "status": "done"
        },
        "046d87a2152507c2055e0a5a369eb4b9": {
          "last_message": "Fetching required fields.",
          "percent_done": 33.53819139596137,
						"url": "http://www.target.com/p/out-of-stock-product",
          "status": "still_processing"
        }
      },
      "info": {
        "name": "Target",
        "url": "target.com"
      }
    }
  },
  "cart_id": "511fa680692d5d9e06000001",
  "message": "still_processing",
  "description": "Still processing."
}
{
  "sites": {
    "5225da1055a0f96c8b000004": {
      "info": {
        "logo": "https://core.twotap.com/system/sites/logos/5225/da10/55a0/f96c/8b00/0004/small/5225da1055a0f96c8b000004.png?1467509483762",
        "name": "Target",
        "url": "target.com"
      },
      "coupon_support": true,
      "gift_card_support": true,
      "checkout_support": ["noauthCheckout", "localCheckout"],
      "shipping_countries_support": ["United States of America", "Japan"],
      "billing_countries_support": ["United States of America", "Japan"],
      "shipping_options": {
        "cheapest": "Default shipping option"
      },
    	"free_shipping_thresholds": {
      	"cheapest": "$25.00"
    	},
      "add_to_cart": {
        "e987cc0e00b50b6edb0b785ca0ce3efb": {
          "title": "RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern",
          "price": "$47.99",
          "original_price": "$79.99",
          "image": "http://target.scene7.com/is/image/Target/12877464?wid=800",
          "alt_images": ["http://target.scene7.com/is/image/Target/12877464?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt01?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt02?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt03?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt04?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt05?wid=800"],
          "description": "Product description",
          "returns": "Return policy",
          "categories": ["home", "dining & entertaining", "flatware", "flatware sets", "RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern"],
          "extra_info": "",
          "pickup_support": true,
          "required_field_values": [{
            "options": {
              "color": {
                "value": "http://scene7.targetimg1.com/is/image/16640462_Swatch",
                "text": "Ebony"
              },
              "size": {
                "value": "2",
                "text": "2"
              }
            },
            "price": "$29.99",
            "image": "http://scene7.targetimg1.com/is/image/16640454?wid=480&hei=480",
            "extra_info": "",
            "alt_images": [
              "http://scene7.targetimg1.com/is/image/16640462",
              "http://scene7.targetimg1.com/is/image/16640462_Alt01"
            ]
          }, {
            "options": {
              "color": {
                "value": "http://scene7.targetimg1.com/is/image/16640462_Swatch",
                "text": "Ebony"
              },
              "size": {
                "value": "4",
                "text": "4"
              }
            },
            "price": "$29.99",
            "image": "http://scene7.targetimg1.com/is/image/16640455?wid=480&hei=480",
            "extra_info": "",
            "alt_images": [
              "http://scene7.targetimg1.com/is/image/16640462",
              "http://scene7.targetimg1.com/is/image/16640462_Alt01"
            ]
          }, {
            "options": {
              "color": {
                "value": "http://scene7.targetimg1.com/is/image/16641944_Swatch",
                "text": "Federal Blue"
              },
              "size": {
                "value": "4",
                "text": "4"
              }
            },
            "price": "$29.99",
            "image": "http://scene7.targetimg1.com/is/image/16641971?wid=480&hei=480",
            "extra_info": "",
            "alt_images": [
              "http://scene7.targetimg1.com/is/image/16641944",
              "http://scene7.targetimg1.com/is/image/16641944_Alt01"
            ]
          }],
          "url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
          "discounted_price": null,
          "required_fields": {
            "size": {
              "data": [{
                "input_name": "SELECT",
                "input_type": "select-one"
              }]
            },
            "quantity": {
              "data": [{
                "input_name": "INPUT",
                "input_type": "text"
              }]
            }
          },
          "status": "done",
          "weight": "1000",
          "clean_url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
          "original_url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605"
        }
      },
      "failed_to_add_to_cart": {
        "046d87a2152507c2055e0a5a369eb4b9": {
          "original_url": "http://www.target.com/p/out-of-stock-product",
          "clean_url": "http://www.target.com/p/out-of-stock-product",
          "status": "failed",
          "discounted_price": null,
          "original_price": null,
          "price": null,
          "pickup_support": false,
          "image": "",
          "url": "http://www.target.com/p/out-of-stock-product"
        }
      }
    }
  },
  "user_id": null,
  "unknown_urls": [],
  "cart_id": "57786aebdfeb41503dcac297",
  "country": "us",
  "stored_field_values": {},
  "message": "has_failures",
  "description": "Some products are out of stock or not supported."
}
{
	"sites": {
		"5225da1055a0f96c8b000004": {
			"info": {
				"logo": "https://core.twotap.com/system/sites/logos/5225/da10/55a0/f96c/8b00/0004/small/5225da1055a0f96c8b000004.png?1467509483762",
				"name": "Target",
				"url": "target.com"
			},
			"coupon_support": true,
			"gift_card_support": true,
			"checkout_support": ["noauthCheckout", "localCheckout"],
			"shipping_countries_support": ["United States of America", "Japan"],
			"billing_countries_support": ["United States of America", "Japan"],
			"shipping_options": {
				"cheapest": "Default shipping option"
			},
    	"free_shipping_thresholds": {
      	"cheapest": "$25.00"
    	},
			"add_to_cart": {
				"e987cc0e00b50b6edb0b785ca0ce3efb": {
					"title": "RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern",
					"price": "$47.99",
					"original_price": "$79.99",
					"image": "http://target.scene7.com/is/image/Target/12877464?wid=800",
					"alt_images": ["http://target.scene7.com/is/image/Target/12877464?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt01?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt02?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt03?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt04?wid=800", "http://target.scene7.com/is/image/Target/12877464_Alt05?wid=800"],
					"description": "Product description",
					"returns": "Return policy",
					"categories": ["home", "dining & entertaining", "flatware", "flatware sets", "RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern"],
					"extra_info": "",
					"pickup_support": true,
					"required_field_values": {
						"color": [{
							"value": "http://scene7.targetimg.com/is/image/16640462_Swatch",
							"text": "Ebony",
							"price": "$29.99",
							"image": "http://scene7.targetimg.com/is/image/16640462?wid=410&hei=410",
							"extra_info": "",
							"dep": {
								"size": [{
									"value": "2",
									"text": "2",
									"price": "$29.99",
									"image": "http://scene7.targetimg.com/is/image/16640454?       wid=480&hei=480",
									"extra_info": "",
									"dep": {}
								}, {
									"value": "4",
									"text": "4",
									"price": "$29.99",
									"image": "http://scene7.targetimg.com/is/image/16640455?wid=480&hei=480",
									"extra_info": "",
									"dep": {}
								}]
							}
						}]
					},
					"url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
					"discounted_price": null,
					"required_fields": {
						"quantity": {
							"input_type": "text",
							"input_name": "INPUT"
						},
						"size": {
							"input_type": "select-one",
							"input_name": "SELECT"
						},
						"color": {
							"input_type": "select-one",
							"input_name": "SELECT"
						}
					},
					"status": "done",
					"clean_url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
					"original_url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605"
				}
			},
			"failed_to_add_to_cart": {
				"046d87a2152507c2055e0a5a369eb4b9": {
					"original_url": "http://www.target.com/p/out-of-stock-product",
					"clean_url": "http://www.target.com/p/out-of-stock-product",
					"status": "failed",
					"discounted_price": null,
					"original_price": null,
					"price": null,
					"pickup_support": false,
					"image": "",
					"url": "http://www.target.com/p/out-of-stock-product",
					"raw": {}
				}
			}
		}
	},
	"user_id": null,
	"unknown_urls": [],
	"cart_id": "57786aebdfeb41503dcac297",
	"country": "us",
	"stored_field_values": {},
	"message": "has_failures",
	"description": "Some products are out of stock or not supported."
}

Query Params

public_token
string
required

Your Two Tap public token.

cart_id
string
required

The cart id that is sent by /cart.

product_attributes_format
string

(Optional) If you set this to flat the product attributes will be returned as an array instead of a tree.

test_mode
string

(Optional) A way to test the API the interface without making actual purchases.

destination_country
string

(Optional) If you know most orders are coming from Japan, for instance, you can set this as your default country to receive prices in local currency.

 

Call /cart/status to see the status of an /cart request. The response is different if response['message'] is still_processing, has_failures, or done.

Response description

{
  "sites": {
    "[site_id]": {
      "info": {
        "logo": "String/URL. URL to the site logo",
        "name": "String. The site's name",
        "url": "String/URL. The site's domain."
      },
      "currency_format": "String. The site's currency format eg: '$AMOUNT'",
      "coupon_support": "Boolean, true or false. If we have coupon support for a certain site.",
      "gift_card_support": "Boolean, true or false. If we have gift card support for a certain site.",
      "checkout_support": "Array. Contains the available checkout types for a site_id. Possible values are 'noauthCheckout', 'localCheckout', and 'authCheckout'.",
      "shipping_countries_support": "Array. Countries where Two Tap supports shipping.",
      "billing_countries_support": "Array. Countries where Two Tap supports billing.",
      "shipping_options": { 
        "shipping option name: 'fastest' or 'cheapest'": "String. Shipping option value eg: '1 business day', '4-9 business days'",
      },
      "free_shipping_thresholds": { 
        "shipping option name: 'fastest' or 'cheapest'": "String/Price. The permanent threshold when shipping becomes free. Doesn't take into consideration temporary free shipping offers.",
      },
      "returns": "String/HTML. The site's return policy. Concatenate with product specific return policy.",
      "add_to_cart": {
        "[product_md5]": {
          "url": "String. URL of the product.",
          "title": "String. The product's title.",
          "brand": "String. Where available the product's brand will be returned. eg: 'Adidas', 'Vans'",
          "price": "String/Price. The product's price.",
          "original_price": "String/Price. The product's original price.",
          "description": "String/HTML. The product description.",
          "image": "String/URL. The product's image URL.",
          "alt_images": "Array. Contains the product's secondary images.",
          "extra_info": "String. Other useful information regarding the product. e.g.: 'Last one available.'",
          "categories": "Array. Contains the product categories.",
          "category_attributes": "Array. Contains special category attributes. eg: ['g-women', 'g-boys', 'g-girls']",
          "site_categories": "Array. Contains the categories on the retailer's site. eg: ['Sale', 'Womens']",
          "weight": "Integer. The product's weight in grams.",
          "pickup_support": "Boolean, true or false. If the store has in-store pickup support.",
          "returns": "String/HTML. The product specific return policy. Concatenate with site-wide return policy.",
          "required_field_values": {
            "[attribute_title eg:'color']": [
              {
                "dep": {
                  "[dependency_attribute_title eg:'size']": [
                    {
                      "dep": {},
                      "extra_info": "String. Other useful information regarding the product. e.g.: 'Last one available.'",
                      "image": "String/URL. The product's image URL",
                      "price": "String/Price. The attribute's price.",
                      "text": "String/Text or URL. Either a string with the attribute name or URL to thumbnail (if color).",
                      "value": "String/Text or URL. Either a string with the attribute value or URL to thumbnail (if color).",
                      "weight": "Integer. Product weight in grams."
                    },
                  ]
                },
                "extra_info": "..",
                "alt_images": "..",
                "image": "..",
                "price": "..",
                "text": "..",
                "value": "..",
                "weight": ".."
              }
            ]
          },
          "required_field_names": "Array. Contains the required field names. In the example detailed above this would be ['color', 'size', 'quantity'].",
          "required_fields": {
            "[field_title eg:'color']": {
              "data": [
                {
                  "input_name": "String. Type of input. eg: 'SELECT', 'INPUT",
                  "input_type": "String. Type of choice. eg: 'select-one', 'text'"
                }
              ]
            },
          },
          "updated_at": "Integer. Timestamp with the last update.",
          "status": "String. The processing status for this product. Can be 'done' or 'still_processing'.",
          "clean_url": "String/URL. This is a canonical version of the URL that Two Tap uses internally for caching, feeds, and more.",
          "original_url": "String/URL. This is the URL you sent to /cart. Use this to match products from Two Tap's response to what you sent.",
        }
      },
      "failed_to_add_to_cart (can be null or an object with the products failed to be added)": {
        "[product_md5]": {
          // Similar to 'add_to_cart'.
          "status": "failed",
        }
      },
    },
  },
  "unknown_urls": "Array. The URLs that were sent with /cart that Two Tap does not support yet.",
  "cart_id": "String. The cart ID.",
  "notes": "Hash/Array/String. The notes that you sent with /cart.",
  "country": "String. What is considered as 'domestic' shipping for this integration.",
  "destination_country": "String. The destination country. eg: 'Japan', 'Romania'",
  "message": "String. If Two Tap has completed the request. eg: 'still_processing', 'has_failures', or 'done'",
  "description": "String. A more human-friendly description of the message."
}
{
  "sites": {
    "[site_id]": {
      "add_to_cart": {
        "[product_id]": {
          "last_message": "String. Contains the last message received.",
          "percent_done": "Float. The stage where the cart is at the moment. Received only if if the status is not 'done'.",
          "url": "String/URL. The product's URL.",
          "status": "String. The status of the product. Can be 'still_processing', 'done', or 'failed'."
        },
      },
      "info": {
        "logo": "String/URL. URL to the site logo",
        "name": "String. The site's name",
        "url": "String/URL. The site's domain."
      }
    }
  },
  "cart_id": "String. The cart ID.",
  "message": "String. If Two Tap has completed the request. eg: 'still_processing', 'has_failures', or 'done'",
  "description": "String. A human readable message. 'Still processing.'"
}
Suggest Edits

/cart/estimates

 
posthttps://api.twotap.com/v1.0/cart/estimates
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

cart_id = ARGV[0]
fields_input = { 
  [site_id]: { 
    addToCart: { 
      [product_md5]: { 
        size: 'M'
      }
    },
    noauthCheckout: {
      shipping_zip: '94303',
      shipping_city: 'City',
      shipping_state: 'State'
    },
    coupons: [
      "coupon_one",
      "coupon_two"
    ],
    shipping_option: 'cheapest'
  } 
}

products = [ 'http://fab.com/sale/4850/product/11263/', 'http://fab.com/sale/4800/product/43565/' ]

response = RestClient.post "https://api.twotap.com/v1.0/cart/estimates?public_token=PUBLIC_TOKEN", { 
  cart_id: cart_id, 
  fields_input: fields_input, 
  products: products 
}

puts response.body
#!/bin/sh

PUBLIC_TOKEN=PUBLIC_TOKEN

curl "https://api.twotap.com/v1.0/cart/estimates?public_token=$PUBLIC_TOKEN" --header "Content-Type: application/json" --data-binary '{
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID: {
      "addToCart": {
        "PRODUCT_MD5": {
          "size": "M" 
        }
      },
      "noauthCheckout": {
        "shipping_zip": "94303",
        "shipping_city": "City",
        "shipping_state": "State"
      },
      "shipping_option": "cheapest"
    }
  }
}'
#!/usr/bin/python

import requests
import json

public_token = 'PUBLIC_TOKEN'
payload = {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "addToCart": {
        "PRODUCT_MD5": {
          "size": "M" 
        }
      },
      "noauthCheckout": {
        "shipping_zip": "94303",
        "shipping_city": "City",
        "shipping_state": "State"
      },
      "shipping_option": "cheapest"
    }
  }
};
r = requests.post('https://api.twotap.com/v1.0/cart/estimates?public_token=' + public_token,
  data=json.dumps(payload), headers={'Content-Type': 'application/json'})
print(r.json())
$.post('https://api.twotap.com/v1.0/cart/estimates?public_token=PUBLIC_TOKEN', {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "addToCart": {
        "PRODUCT_MD5": {
          "size": "M" 
        },
        "noauthCheckout": {
          "shipping_zip": "94303",
          "shipping_city": "City",
          "shipping_state": "State"
        },
        "shipping_option": "cheapest"
      }
    }
  }
}, function(data, status) {
    console.log(data);
});
var request = require("request");
var public_token = 'PUBLIC_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/cart/estimates?public_token=' + public_token,
  json: {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "addToCart": {
        "PRODUCT_MD5": {
          "size": "M" 
        }
      },
      "noauthCheckout": {
        "shipping_zip": "94303",
        "shipping_city": "City",
        "shipping_state": "State"
      },
      "shipping_option": "cheapest"
    }
  }
},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$products = ['http://fab.com/sale/4850/product/11263/', 'http://fab.com/sale/4800/product/43565/'];
$cartId = 'CART_ID';
$fieldsInput = [
  "SITE_ID" => [
    "addToCart" => [
      "PRODUCT_MD5" => [
        "size" => "M"
      ]
    ],
    "noauthCheckout" => [
      "shipping_zip" => "94303",
      "shipping_city" => "City",
      "shipping_state" => "State"
    ],
    "shipping_option" => "cheapest"
  ]
];

$estimates = $api->cart()->estimates($cartId, $fieldsInput);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    url := "https://api.twotap.com/v1.0/cart/estimates?public_token=" + public_token
    var jsonStr = []byte(`{
      "cart_id": "CART_ID",
      "fields_input": {
        "SITE_ID": {
          "addToCart": {
            "PRODUCT_MD5": {
              "size": "M" 
            }
          },
          "noauthCheckout": {
            "shipping_zip": "94303",
            "shipping_city": "City",
            "shipping_state": "State"
          },
          "shipping_option": "cheapest"
        }
      }
    }`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    req.Header.Add("Content-Type", "application/json")
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var cart_id = "CART_ID";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/cart/estimates?public_token=" + public_token);
      dynamic payload = new ExpandoObject ();
      payload.cart_id = cart_id;
      payload.fields_input = new ExpandoObject ();
      payload.fields_input.SITE_ID = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart.PRODUCT_MD5 = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart.PRODUCT_MD5.size = "M";
      payload.fields_input.SITE_ID.noauthCheckout = new ExpandoObject ();
      payload.fields_input.SITE_ID.noauthCheckout.shipping_zip = "94303";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_city = "City";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_state = "State";
      payload.fields_input.shipping_option = "cheapest";
      string json = JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json, Encoding.UTF8, "application/json")).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{
  "message": "done",
  "estimates": { 
    "53fb0d1fce04fa71f5000003": {
      "prices": {
        "shipping_price": "$8.00",
        "subtotal": "$50.00",
        "sales_tax": "$6.09",
        "final_price": "$64.09"
      },
      "coupons": {},
      "delivery_days": {
        "min": 1,
        "max": 5
      }
    }
  },
  "estimated_total_prices": {
    "shipping_price": "$8.00",
    "subtotal": "$50.00",
    "sales_tax": "$6.09",
    "final_price": "$64.09"
  },
  "estimated_delivery_days": {
    "min": 1,
    "max": 5
  },
  "coupons": {
    "53fb0d1fce04fa71f5000003": {}
  },
  "destination": "domestic"
}
{
  "message":"done",
  "estimates":{
    "51bf1d1055a0f9e0d0000003":{
      "prices":{
        "shipping_price":"$5.00",
        "sales_tax":"$2.54",
        "coupon_value": "$3.00",
        "final_price":"$31.54"
      },
      "coupons":{
        "coupon_one":{
          "label":"30% off any one item.",
          "status":"valid"
        },
        "coupon_two":{
          "label":"Invalid: $10 off any purchase",
          "status":"invalid"
        }
      }
    }
  },
  "estimated_total_prices": {
    "shipping_price": "$5.00",
    "sales_tax": "$2.54",
    "coupon_value": "$3.00",
    "final_price": "$31.54"
  },
  "coupons":{
    "coupon_one":{
      "label":"30% off any one item.",
      "status":"valid"
    },
    "coupon_two":{
      "label":"Invalid: $10 off any purchase",
      "status":"invalid"
    }
  }
}
{
  "message": "done",
  "estimates": null,
  "estimated_total_prices": {
    "shipping_price": "2263.60 JPY",
    "subtotal": "5125.90 JPY",
    "sales_tax": "410.07 JPY",
    "final_price": "8055.86 JPY",
    "duties": "256.29 JPY"
  },
  "estimated_delivery_days": {
    "min": 1,
    "max": 5
  },
  "coupons": {
    "53fb0d1fce04fa71f5000003": {
      "WINTER": {
        "label": "10% off over 150$",
        "status": "valid"
      }
    }
  },
  "destination": "intl"
}

Body Params

public_token
string
required

Your Two Tap public token.

cart_id
string
required

A cart id that has finished running.

fields_input
object

A hash containing purchase information by site_id.

products
array of strings

(Optional) An array of product URLs that have accepted by the user. This is useful in case a user removes some items between /cart and /purchase. If specified fields_input should contain only the required data for those products. These are Two Tap urls from /cart/status, and NOT the original_urls that you originally sent us.

destination_country
string

(Optional) If the customer hasn't entered their full information yet you can set up a default country to get landed cost for.

 

This endpoint will return an estimated sales tax, shipping price, and final price for a certain cart.

The more information you can give it the more accurate it becomes.

  • Send the shopper selected product options. Some product attribute combinations have different prices so in order to have a valid estimates the attributes are recommended.
  • Send the shoppers shipping city, state, and zip code accurate sales tax calculations.

Domestic orders will contain a breakdown of prices per site, the estimates variable.

International orders will only contain the aggregate, the estimated_total_prices variable.

The 'fields_input' parameter

The fields_input parameter is a hash that looks like below:

{
  "[site_id]":
    {
      "noauthCheckout": { "required checkout fields" },
      "addToCart": {
        "[productMD5_1]": "required product fields",
        "[productMD5_2]": "required product fields",
      },
      "coupons": [
        "coupon_one",
        "coupon_two"
      ],
      "shipping_option (optional)": { "cheapest or fastest" },
    }
},

Response description

{
  "message": "String. If Two Tap has completed the request. eg: 'done', 'still_processing'",
  // estimates available for domestic purchases.
  "estimates": { 
    "[site_id]": {
      "prices": {
        "shipping_price": "String/Price. Shipping value.",
        "sales_tax": "String/Price. Sales tax value.",
        "discount_value": "String/Price. Discount value.",
        "final_price": "String/Price. Final price value.",
        "coupon_value": "String/Price or String. If the coupon statuses are 'valid' this will be an amount. Otherwise, it will be a text that says: 'applied at checkout'."
      },
    },
  },
  // estimated_total_prices available for both domestic and international purchases.
  "estimated_total_prices": { 
    "subtotal": "String/Price. Subtotal value.",
    "coupon_value": "String/Price. If the coupon statuses are 'valid' this will be an amount. Otherwise, it will be a text that says: 'applied at checkout'.",
    "shipping_price": "String/Price. Shipping value.",
    "sales_tax": "String/Price. Sales tax value.",
    "final_price": "String/Price. Final price value.",
    "discount_value": "String/Price. Discount value.",
    "duties": "String/Price. Duties value."
  },
  "estimated_delivery_days":{
    "min": "Integer. Minimum number of days.",
    "max": "Integer. Maximum number of days."
  },
  "destination": "String. Destination of purchase. eg: 'domestic', 'intl'"
}
{
  "message": "String. If Two Tap has completed the request. eg: 'done', 'still_processing'",
  // estimates available for domestic purchases.
  "estimates": { 
    "[site_id]": {
      "prices": {
        "shipping_price": "String/Price. Shipping value.",
        "sales_tax": "String/Price. Sales tax value.",
        "discount_value": "String/Price. Discount value.",
        "final_price": "String/Price. Final price value.",
        "coupon_value": "String/Price or String. If the coupon statuses are 'valid' this will be an amount. Otherwise, it will be a text that says: 'applied at checkout'."
      },
    },
  },
  // estimated_total_prices available for both domestic and international purchases.
  "estimated_total_prices": { 
    "subtotal": "String/Price. Subtotal value.",
    "coupon_value": "String/Price. If the coupon statuses are 'valid' this will be an amount. Otherwise, it will be a text that says: 'applied at checkout'.",
    "shipping_price": "String/Price. Shipping value.",
    "sales_tax": "String/Price. Sales tax value.",
    "final_price": "String/Price. Final price value.",
    "discount_value": "String/Price. Discount value.",
    "duties": "String/Price. Duties value."
  },
  "estimated_delivery_days":{
    "min": "Integer. Minimum number of days.",
    "max": "Integer. Maximum number of days."
  },
  "coupons": {
    "[site_id]":{
      "[coupon_code]": {
        "status": "String. The status which can be 'invalid', 'valid', or 'unknown'.",
        "label": "String. A short label about the coupon. eg: '10% of any one product'. Present if status is 'valid' or 'invalid'."
      }
    },
  },  
  "destination": "String. Destination of purchase. eg: 'domestic', 'intl'"
}
{
  "message": "String. If Two Tap has completed the request. eg: 'done', 'still_processing'",
  // estimated_total_prices available for both domestic and international purchases.
  "estimated_total_prices": { 
    "subtotal": "String/Price. Subtotal value.",
    "coupon_value": "String/Price. If the coupon statuses are 'valid' this will be an amount. Otherwise, it will be a text that says: 'applied at checkout'.",
    "shipping_price": "String/Price. Shipping value.",
    "sales_tax": "String/Price. Sales tax value.",
    "final_price": "String/Price. Final price value.",
    "discount_value": "String/Price. Discount value.",
    "duties": "String/Price. Duties value."
  },
  "estimated_delivery_days":{
    "min": "Integer. Minimum number of days.",
    "max": "Integer. Maximum number of days."
  },
  "coupons": {
    "[site_id]":{
      "[coupon_code]": {
        "status": "String. The status which can be 'invalid', 'valid', or 'unknown'.",
        "label": "String. A short label about the coupon. eg: '10% of any one product'. Present if status is 'valid' or 'invalid'."
      }
    },
  },  
  "destination": "String. Destination of purchase. eg: 'domestic', 'intl'"
}
Suggest Edits

/purchase

 
posthttps://api.twotap.com/v1.0/purchase

#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

cart_id = ARGV[0]

fields_input = { 
  [site_id]: {
    "noauthCheckout": { "email" => "shopper@gmail.com", "shipping_telephone" => "6503941234", "shipping_zip" => "94303", "shipping_state" => "California", "shipping_city" => "Palo Alto", "shipping_address" => "555 Palo Alto Avenue", ".." => ".." },
    "addToCart": {
      [product_md5]: { "quantity": 1 }
    }
  }
}

affiliate_links = {
  [site_id]: "http://affiliate_link"  
}

products = [ 'http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605' ]

response = RestClient.post "https://api.twotap.com/v1.0/purchase?public_token=PUBLIC_TOKEN", { 
  cart_id: cart_id, 
  fields_input: fields_input, 
  affiliate_links: affiliate_links, 
  products: products 
}

puts response.body

#!/bin/sh

PUBLIC_TOKEN=PUBLIC_TOKEN

curl "https://api.twotap.com/v1.0/purchase?public_token=$PUBLIC_TOKEN" --header "Content-Type: application/json" --data-binary '
{
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
   "SITE_ID": "http://affiliate_link"  
  },
  "products": [ "http://fab.com/sale/4850/product/11263" ]
}'
#!/usr/bin/python

import requests
import json

public_token = 'PUBLIC_TOKEN'
payload = {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
   "SITE_ID": "http://affiliate_link"  
  },
  "products": [ "http://fab.com/sale/4850/product/11263" ]
};
r = requests.post('https://api.twotap.com/v1.0/purchase?public_token=' + public_token,
  data=json.dumps(payload), headers={'Content-Type': 'application/json'})
print(r.json())
$.post('https://api.twotap.com/v1.0/purchase?public_token=PUBLIC_TOKEN', {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
   "SITE_ID": "http://affiliate_link"  
  },
  "products": [ "http://fab.com/sale/4850/product/11263" ]
}, function(data, status) {
    console.log(data);
});
var request = require("request");
var public_token = 'PUBLIC_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/purchase?public_token=' + public_token,
  json: {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
  "SITE_ID": "http://affiliate_link"  
  },
  "products": [ "http://fab.com/sale/4850/product/11263" ]
},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$cartId = 'CART_ID';
$fieldsInput = [
  "SITE_ID" => [
    "noauthCheckout" => [
      "email" => "shopper@gmail.com",
      "shipping_telephone" => "6503941234",
      "shipping_zip" => "94303",
      "shipping_state" => "California",
      "shipping_city" => "Palo Alto",
      "shipping_address" => "555 Palo Alto Avenue",
      ".." => ".."
    ],
    "addToCart" => [
      "[product_md5]" => [
        "quantity" => 1
      ]
    ]
  ]
];
$affiliateLinks = [
  "SITE_ID" => "http://affiliate_link"
];
$products = [
  "http://fab.com/sale/4850/product/11263"
];

$response = $api->purchase()->create($cartId, $fieldsInput, $affiliateLinks, $confirm, $products);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    url := "https://api.twotap.com/v1.0/purchase?public_token=" + public_token
    var jsonStr = []byte(`{
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
  "SITE_ID": "http://affiliate_link"  
  },
  "products": [ "http://fab.com/sale/4850/product/11263" ]
}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    req.Header.Add("Content-Type", "application/json")
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var cart_id = "CART_ID";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/purchase?public_token=" + public_token);
      dynamic payload = new ExpandoObject ();
      payload.cart_id = cart_id;
      payload.fields_input = new ExpandoObject ();
      payload.fields_input.SITE_ID = new ExpandoObject ();
      payload.fields_input.SITE_ID.noauthCheckout = new ExpandoObject ();
      payload.fields_input.SITE_ID.noauthCheckout.email = "shopper@gmail.com";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_telephone = "6503941234";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_zip = "94303";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_city = "Palo Alto";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_state = "California";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_address = "555 Palo Alto Avenue";
      payload.fields_input.SITE_ID.addToCart = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart.PRODUCT_MD5 = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart.PRODUCT_MD5.quantity = 1;
      payload.affiliate_links = new ExpandoObject ();
      payload.affiliate_links.SITE_ID = "http://affiliate_link";
      string json = JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json, Encoding.UTF8, "application/json")).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{
  "purchase_id": "50f414b9e6a4869bf6000010",
  "message": "still_processing",
  "description": "Still processing."
}

Body Params

public_token
string
required

Your Two Tap public token.

cart_id
string
required

The cart id that is sent by /cart.

fields_input
object
required

A hash containing purchase information by site_id.

affiliate_links
object

(Optional) A hash containing affiliate links by site_id.

confirm
object

(Optional but highly recommended) Instead of polling /purchase/status Two Tap can send callbacks as it's processing the purchase.

products
array of strings

(Optional) An array of product URLs that have been accepted by the user. This is useful in case an user removes some items between /cart and /purchase. If specified, fields_input should contain only the required data for those products. These are Two Tap urls from /cart/status, and NOT the original_urls you originally sent us.

remove_oos
boolean

(Optional) Default false. This tells Two Tap to remove any out of stock products that it encounters.

notes
object

(Optional) You can send any information here and it will be attached to the purchase and piped back with /status responses and callbacks.

test_mode
string

(Optional) A way to test the API the interface without making actual purchases.

locale
string

(Optional) For INTL orders Two Tap sends status update emails to consumers. Set a language for the emails here. ISO 639-1.

user_token
string

(Optional) Specify a shared or private user_token to use the wallet.

store_in_wallet
boolean

(Optional) Default true. If Two Tap should store in the wallet the information in fields_input when user_token is used.

 

Purchase will retrieve the shipping price, sales tax, and final price of the order. It will start the checkout process but not finish it to ensure the data sent back is valid.

Two Tap can support three checkout types: noauthCheckout (guest checkout), localCheckout (pickup from store), and authCheckout (with a store account, not recommended). You can find what checkouts types the store has enabled by looking in /cart/status at the checkout_support attribute for each site_id.

This request is processed in the background which means you either have to call /purchase/status (until message is not still_processing) or use the SMS API flow or HTTP API flow to receive callbacks.

Once this request finishes you will have 5 minutes available to call /purchase/confirm to confirm it.

When implementing Two Tap take the asynchronous approach to placing orders and avoid polling /purchase/status. Request all the necessary information, start a /purchase, display a message to the shopper saying "Your order is being placed, you'll receive a confirmation shortly", and let her leave the experience.

Two Tap will send the order in the background and via the SMS API flow or HTTP API flow will call your endpoints at different steps in the process.

This is very similar to the Amazon approach where your order information is accepted instantly and if it wasn't valid you receive an email five minutes later.

If you are using the API directly we highly recommend you use an address validator like lob.com or easypost.com in your app/service. It's free.

The 'fields_input' parameter

The fields_input parameter is a hash that looks like below:

{
  "[site_id]":
    {
      "noauthCheckout": { "required checkout fields" },
      "addToCart": {
        "[productMD5_1]": "required product fields",
        "[productMD5_2]": "required product fields",
      },
      "coupons": [ "coupon value 1", "coupon value 2" ], // (optional)
      "gift_cards": [ { "number": "number", "pin": "pin" } ], // (optional)
      "shipping_option": { "cheapest or fastest" }, // (optional)
    },
  "[site_id_2]": { .. }
}
{
  "[site_id]":
    {
      "authCheckout": { "required checkout fields" },
      "login": { "username": "store account username or email", "password": "store account password" },
      "addToCart": {
        "[productMD5_1]": "required product fields",
        "[productMD5_2]": "required product fields",
      },
      "coupons": [ "coupon value 1", "coupon value 2" ], // (optional)
      "gift_card": { "number": "number", "pin": "pin") }, // optional
      "shipping_option": { "cheapest or fastest" }, // optional
    },
  "[site_id_2]": { .. }
}
{
  "[site_id]":
    {
      "localCheckout": { "required checkout fields" },
      "addToCart": {
        "[productMD5_1]": "required product fields",
        "[productMD5_2]": "required product fields",
      },
      "pickup": {
        "[productMD5_1]": "Mountain View",
        "[productMD5_2]": "Mountain View",
      },
      "coupons": [ "coupon value 1", "coupon value 2" ], // (optional)
      "gift_card": { "number": "number", "pin": "pin") }, // optional
    },
  "[site_id_2]": { .. }
}

To keep things simple if you're building your own native checkout always ask for the following fields, and send them all with each site_id.

 [ 
    'email',
    'shipping_first_name',
    'shipping_last_name',
    'shipping_address',
    'shipping_city',
    'shipping_state',
    'shipping_country',
    'shipping_zip',
    'shipping_telephone',
    'billing_first_name',
    'billing_last_name',
    'billing_address',
    'billing_city',
    'billing_state',
    'billing_country',
    'billing_zip',
    'billing_telephone',
    'card_type',
    'card_number',
    'card_name',
    'expiry_date_year',
    'expiry_date_month',
    'cvv'
  ]
 [ 
    'email',
    'shipping_first_name', // this is the designated pickup person.
    'shipping_last_name',
    'shipping_zip', // this is the zip for the pickup store.
    'billing_first_name',
    'billing_last_name',
    'billing_address',
    'billing_city',
    'billing_state',
    'billing_country',
    'billing_zip',
    'billing_telephone',
    'card_type',
    'card_number',
    'card_name',
    'expiry_date_year',
    'expiry_date_month',
    'cvv'
  ]

Some fields require certain formats

For instance, a US state should be sent as 'New York' and not 'NY'.

The possible keys and values are listed here.

Addresses

Due to the way most retailers handle validations (which is turn a factor of the carriers) the only characters allowed for addresses are alphanumerical, comma, dot, space, dash, and /. You can use /fields_input_validate to check your information ahead of time.

To break an address into address_line_1 and address_line_2 add one comma, eg:
"106 Shotwell St, Unit 41"

The 'affiliate_links' parameter

The affiliate_links parameter is a hash that looks like below:

{ 
  "[site_id]": "affiliate_link" 
}

The 'confirm' parameter

Instead of polling /purchase/status Two Tap can send callbacks as it's processing the purchase. See the SMS API flow or HTTP API flow.

 { 
   "method": "http", 
   "http_confirm_url": "https://your_confirm_endpoint",
   "http_update_url": "https://your_update_endpoint",
   "skip_confirm": "(optional and dangerous) YOUR_PRIVATE_TOKEN"
 }
Suggest Edits

/purchase/status

 
gethttps://api.twotap.com/v1.0/purchase/status
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.get "https://api.twotap.com/v1.0/purchase/status?public_token=PUBLIC_TOKEN&purchase_id=#{purchase_id}"

puts response.body
#!/bin/bash

PUBLIC_TOKEN=PUBLIC_TOKEN
PURCHASE_ID=PURCHASE_ID

curl https://api.twotap.com/v1.0/purchase/status?public_token=$PUBLIC_TOKEN\&purchase_id=$PURCHASE_ID
#!/usr/bin/python

import requests

public_token = 'PUBLIC_TOKEN'
purchase_id = 'PURCHASE_ID'
r = requests.get('https://api.twotap.com/v1.0/purchase/status?public_token=' + public_token + '&purchase_id=' + purchase_id)
print(r.json())
$.get("https://api.twotap.com/v1.0/purchase/status?public_token=PUBLIC_TOKEN&purchase_id=PURCHASE_ID", function(data, status) {
  console.log(data);
})
var request = require("request");
var public_token = 'PUBLIC_TOKEN';
var purchase_id = 'PURCHASE_ID';
request({
  url: 'https://api.twotap.com/v1.0/purchase/status?public_token=' + public_token + '&purchase_id=' + purchase_id,
  method: "GET"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php 

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$purchaseId = 'PURCHASE_ID';

$status = $api->purchase()->status($purchaseId);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    purchase_id := "PURCHASE_ID"
    url := "https://api.twotap.com/v1.0/purchase/status?public_token=" + public_token + "&purchase_id=" + purchase_id
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var purchase_id = "PURCHASE_ID";
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/purchase/status?public_token=" + public_token + "&purchase_id=" + purchase_id);
      var result = client.GetAsync ("").Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{
  "purchase_id": "57786063dfeb41503dcac289",
  "cart_id": "59bf99659674e662b2f39e32",
  "user_id": null,
  "created_at": "2016-07-03T00:46:27.583Z",
  "destination": "domestic",
  "test_mode": null,
  "notes": null,
  "used_profiles": null,
  "unique_token": "3043",
  "sites": {
    "5225da1055a0f96c8b000004": {
      "info": {
        "url": "target.com",
        "name": "Target",
        "logo": "https://core.twotap.com/system/sites/logos/5225/da10/55a0/f96c/8b00/0004/small/5225da1055a0f96c8b000004.png?1467506740775"
      },
      "prices": {},
      "details": {},
      "failed_to_add_to_cart": null,
      "order_id": null,
      "products": {
        "e987cc0e00b50b6edb0b785ca0ce3efb": {
          "title": "RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern",
          "price": "$47.99",
          "original_price": "$79.99",
          "image": "http://target.scene7.com/is/image/Target/12877464?wid=800",
          "alt_images": ["http://target.scene7.com/is/image/Target/12877464?wid=800"],
          "description": "Product description",
          "returns": "Return policy",
          "categories": ["home", "dining & entertaining", "flatware", "flatware sets", "RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern"],
          "extra_info": "",
          "pickup_support": true,
          "required_field_names": ["size", "quantity"],
          "required_field_values": {
            "size": [{
              "extra_info": "",
              "image": "http://target.scene7.com/is/image/Target/12877464?wid=800",
              "price": "$47.99",
              "text": "A",
              "value": "A"
            }]
          },
          "url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",

          "required_fields": {
            "size": {
              "data": [{
                "input_name": "SELECT",
                "input_type": "select-one"
              }]
            },
            "quantity": {
              "data": [{
                "input_name": "INPUT",
                "input_type": "text"
              }]
            }
          },
          "status": "done",
          "clean_url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
          "original_url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
          "input_fields": {
            "quantity": "1",
            "size": "H"
          }
        }
      },
      "status": "still_processing",
      "last_message": "Adding product RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern.",
      "percent_done": 22.470238095238095
    }
  },
  "message": "still_processing",
  "description": "Still processing."
}
{
  "purchase_id": "57786183dfeb41503dcac292",
  "cart_id": "59bf99659674e662b2f39e32",
  "user_id": null,
  "created_at": "2016-07-03T00:51:15.807Z",
  "total_prices": {
    "final_price": "$52.19",
    "shipping_price": "$0.00",
    "sales_tax": "$4.20"
  },
  "destination": "domestic",
  "test_mode": null,
  "notes": null,
  "used_profiles": null,
  "unique_token": "7034",
  "pending_confirm": false,
  "sites": {
    "5225da1055a0f96c8b000004": {
      "info": {
        "url": "target.com",
        "name": "Target",
        "logo": "https://core.twotap.com/system/sites/logos/5225/da10/55a0/f96c/8b00/0004/small/5225da1055a0f96c8b000004.png?1467506923931"
      },
      "prices": {
        "final_price": "$52.19",
        "shipping_price": "free",
        "sales_tax": "$4.20"
      },
      "details": {
        "shipping_estimate": "RiverRidge® 46 Pc Personalized Flatware - Bouquet Pattern - H by 07/13",
        "coupons": {
          "COUPON1": {
            "status": "valid"
          },
          "COUPON2": {
            "status": "invalid"
          }
        }
      },
      "failed_to_add_to_cart": null,
      "products": {
        "e987cc0e00b50b6edb0b785ca0ce3efb": {
          "title": "RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern",
          "price": "$47.99",
          "original_price": "$79.99",
          "image": "http://target.scene7.com/is/image/Target/12877464?wid=800",
          "alt_images": ["http://target.scene7.com/is/image/Target/12877464?wid=800" ], 
          "description": "Product description",
          "returns": "Return policy",
          "categories": ["home", "dining & entertaining", "flatware", "flatware sets", "RiverRidge® 46 Pc Personalized Flatware Set - Bouquet Pattern"],
          "extra_info": "",
          "pickup_support": true,
          "required_field_names": ["size", "quantity"],
          "url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
          "required_fields": {
            "size": {
              "data": [{
                "input_name": "SELECT",
                "input_type": "select-one"
              }]
            },
            "quantity": {
              "data": [{
                "input_name": "INPUT",
                "input_type": "text"
              }]
            }
          },
          "status": "done",
          "clean_url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
          "original_url": "http://www.target.com/p/46-pc-bouquet-personalized-flatware-set/-/A-12876605",
          "input_fields": {
            "size": "H",
            "quantity": "1"
          }
        }
      },
      "status": "failed",
      "status_messages": ["We're sorry. The credit card you entered could not be processed. Please double-check the information and enter it again or try a different credit card. If you're sure the information you provided is correct, please contact your credit card company."],
      "status_reason": "bad_payment"
    }
  },
  "message": "has_failures"
}

Query Params

public_token
string
required

Your Two Tap public token.

purchase_id
string
required

The purchase's id.

test_mode
string

(Optional) A way to test the API the interface without making actual purchases.

 

Call /purchase/status to see the status of a /purchase request. The response is different if response['message'] is still_processing, has_failures, or done.

Response description

{
  "purchase_id": "String. The purchase id.",
  "cart_id": "String. The cart id that was sent with the purchase.",
  // state only available for international purchases."
  "state": "String. The state of the purchase. eg: 'info_running', 'info_finished', 'confirm_running', 'confirm_finished', 'waiting_for_tracking_links', 'to_warehouse', 'to_shopper', or 'cancelled'.",
  "pending_confirm": "Boolean, true or false. Whether /purchase/confirm was called.",
  "destination": "String. Destination of purchase. eg: 'domestic', 'intl'",
  "notes": "Hash/Array/String. The notes that you sent with /cart.",
  // total_prices available for both domestic and international purchases.
  // These values are recalculated before the purchase is finalized.
  // Meaning, it could be an empty hash if all stores return errors
  // like, for instance, bad payment.
  "total_prices": { 
    "shipping_price": "String/Price. Shipping value.",
    "sales_tax": "String/Price. Sales tax value.",
    "coupon_value": "String/Price. Coupon value.",
    "discount_value": "String/Price. Discount value for INTL orders.",
    "duties": "String/Price. Duties value for INTL orders.",
    "final_price": "String/Price. Final price value.",
  },
  "sites": {
    "[side_id]": {
      "info": {
        "logo": "String/URL. URL to the site logo",
        "name": "String. The site's name",
        "url": "String/URL. The site's domain."
      },
       // prices only available for domestic purchases.
       // These values are retrieved from the retailer site
       // right before asking for confirmation.
      "prices": {
        "shipping_price": "String/Price. Shipping price value. eg: '$52.19', 'free'",
        "sales_tax": "String/Price. Sales tax value",
        "coupon_value": "String/Price. Sales tax value",
        "gift_card_value": "String/Price. Sales tax value",
        "final_price": "String/Price. Final price value",
      },
      // details only available for domestic purchases.
      "details": {
        "shipping_estimate": "String. The store's estimated delivery date. eg: 'Express delivery service from [store name], order before 12.00 for delivery next day'",
        "coupons": {
          "[coupon_name]": {
            "status": "String. Status of coupon. eg: 'valid', 'invalid'"
          },
        },
      },
      "products": {
        "[product_md5]": {
          // Similar to the /cart/status response.
        }
      },
      "removed_products": {
        "[product_md5]": {
           // Two Tap will remove out of stock products on INTL purchases or if 'remove_oos' is set to true. Similar to the /cart/status response.
        }
      },
      "failed_to_add_to_cart": "String. In case the purchase failed because Two Tap couldn't add a product to the cart this will contain that product's MD5. Match this with /cart/status responses.",
      // order_id only available for domestic purchases.
      "order_id": "String. After 'confirm' finished this could contain the site's order id.",
      "status": "String. The site status. For domestic orders this can be: 'still_processing', 'failed' or 'done'. For international orders this can be: 'still_processing', 'has_failures' (if Two Tap removed products), 'failed', or 'done'.",
      "status_reason": "String. If the status has failed this field will tell you why. eg: 'bad_payment', 'bad_billing_address', 'bad_shipping_address', 'verified_visa_3d_secure', 'bad_login', 'oos' (out of stock), 'session_expired', or 'other'"
    }
  },
  "message": "String. If Two Tap has completed the request. This can be: 'still_processing', 'has_failures', or 'done'.",
  "description": "String. A more human readable explanation: eg. 'Still processing'"
}
{
  "purchase_id": "String. The purchase id.",
  "destination": "String. Destination of purchase. eg: 'domestic', 'intl'",
  "notes": "Hash/Array/String. The notes that you sent with /cart.",
  "sites": {
    "[site_id]": {
      "info": {
        "logo": "String/URL. URL to the site logo",
        "name": "String. The site's name",
        "url": "String/URL. The site's domain."
      },
      "failed_to_add_to_cart": "String. In case the purchase failed because Two Tap couldn't add a product to the cart this will contain that product's MD5. Match this with /cart/status responses.",
      // order_id only available for domestic purchases.
      "order_id": "String. After 'confirm' finished this could contain an order id. ",
      "products": {
        "[product_md5]": {
          // Similar to the /cart/status response.
        }
      },
      "status": "String. The site status. For domestic orders this can be: 'still_processing', 'failed' or 'done'. For international orders this can be: 'still_processing', 'has_failures' (if Two Tap removed products), 'failed', or 'done'.",
      "status_reason": "String. If the status has failed this field will tell you why. eg: 'bad_payment', 'bad_billing_address', 'bad_shipping_address', 'verified_visa_3d_secure', 'oos' (out of stock), 'session_expired', or 'other'",
      "last_message": "String. The latest status message sent by the request processor. This is used during the 'still_processing' phase.",
      "percent_done": "Float. The stage where the cart is at the moment. Received only if if the status is not 'done'."
    }
  },
  "message": "String. If Two Tap has completed the request. This can be: 'still_processing', 'has_failures', or 'done'.",
  "description": "String. A more human readable explanation: eg. 'Still processing'"
}
Suggest Edits

/purchase/confirm

 
posthttps://api.twotap.com/v1.0/purchase/confirm
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.post "https://api.twotap.com/v1.0/purchase/confirm?private_token=PRIVATE_TOKEN", { purchase_id: purchase_id }

puts response.body
#!/bin/bash

PRIVATE_TOKEN=PRIVATE_TOKEN
PURCHASE_ID=PURCHASE_ID

curl https://api.twotap.com/v1.0/purchase/confirm?private_token=$PRIVATE_TOKEN --header 'Content-Type: application/json' --data-binary '{"purchase_id" : "$PURCHASE_ID"}'
#!/usr/bin/python

import requests
import json

private_token = 'PRIVATE_TOKEN'
payload = {"purchase_id": "PURCHASE_ID"}
r = requests.post('https://api.twotap.com/v1.0/purchase/confirm?private_token=' + private_token,
  data=json.dumps(payload), headers={'Content-Type': 'application/json'})
print(r.json())
$.post('https://api.twotap.com/v1.0/purchase/confirm?private_token=PRIVATE_TOKEN', {
  "purchase_id": "PURCHASE_ID"
}, function(data, status) {
    console.log(data);
});
var request = require("request");
var private_token = 'PRIVATE_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/purchase/confirm?private_token=' + private_token,
  json: {"purchase_id": "PURCHASE_ID"},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$purchaseId = 'PURCHASE_ID';

$response = $api->purchase()->confirm($purchaseId);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    private_token := "PRIVATE_TOKEN"
    url := "https://api.twotap.com/v1.0/purchase/confirm?private_token=" + private_token
    var jsonStr = []byte(`{"purchase_id": "PURCHASE_ID"}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var private_token = "PRIVATE_TOKEN";
      var purchase_id = "PURCHASE_ID";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/purchase/confirm?private_token=" + private_token);
      dynamic payload = new ExpandoObject ();
      payload.purchase_id = purchase_id;
      string json = JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json, Encoding.UTF8, "application/json")).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{
  "purchase_id": "50f414b9e6a4869bf6000010",
  "message": "still_processing",
  "description": "Still processing."
}

Body Params

private_token
string
required

Your Two Tap PRIVATE token.

purchase_id
string
required

The purchase id that is sent by /purchase.

test_mode
string

(Optional) fake_confirm. A way to test the API the interface without making actual purchases.

 

Purchase confirm finalizes the order. Perform this request server side as it uses your private token.

This request is available for 5 minutes after Two Tap calls your confirm callback or /purchase/status is in the pending_confirm: true state.

The confirmation is processed in the background which means you either have to call /purchase/status (until message is not still_processing) or use the SMS API flow or HTTP API flow to receive callbacks.

Response description

{
  "purchase_id": "String. The purchase id.",
  "message": "String. If Two Tap has completed the request. eg: 'still_processing'",
  "description": "String. A more human-friendly description of the message. eg: 'Purchase is processing.'"
}
Suggest Edits

/purchase/abort

 
posthttps://api.twotap.com/v1.0/purchase/abort
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.post "https://api.twotap.com/v1.0/purchase/abort?private_token=PRIVATE_TOKEN", { purchase_id: purchase_id }

puts response.body
#!/bin/bash

PRIVATE_TOKEN=PRIVATE_TOKEN
PURCHASE_ID=PURCHASE_ID

curl https://api.twotap.com/v1.0/purchase/abort?private_token=$PRIVATE_TOKEN --header 'Content-Type: application/json' --data-binary '{"purchase_id" : "$PURCHASE_ID"}'
#!/usr/bin/python

import requests
import json

private_token = 'PRIVATE_TOKEN'
payload = {"purchase_id": "PURCHASE_ID"}
r = requests.post('https://api.twotap.com/v1.0/purchase/abort?private_token=' + private_token,
  data=json.dumps(payload), headers={'Content-Type': 'application/json'})
print(r.json())
$.post('https://api.twotap.com/v1.0/purchase/abort?private_token=PRIVATE_TOKEN', {
  "purchase_id": "PURCHASE_ID"
}, function(data, status) {
    console.log(data);
});
var request = require("request");
var private_token = 'PRIVATE_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/purchase/abort?private_token=' + private_token,
  json: {"purchase_id": "PURCHASE_ID"},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$purchaseId = 'PURCHASE_ID';

$response = $api->purchase()->abort($purchaseId);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    private_token := "PRIVATE_TOKEN"
    url := "https://api.twotap.com/v1.0/purchase/abort?private_token=" + private_token
    var jsonStr = []byte(`{"purchase_id": "PURCHASE_ID"}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var private_token = "PRIVATE_TOKEN";
      var purchase_id = "PURCHASE_ID";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/purchase/abort?private_token=" + private_token);
      dynamic payload = new ExpandoObject ();
      payload.purchase_id = purchase_id;
      string json = JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json, Encoding.UTF8, "application/json")).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{
  "purchase_id": "50f414b9e6a4869bf6000010",
  "message": "done",
}

Body Params

private_token
string
required

Your Two Tap PRIVATE token.

purchase_id
string
required

The purchase id that is sent by /purchase.

 

This endpoint stops a purchase that hasn't been confirmed yet via /purchase/confirm. The purchase will enter a 'cancelled' state and, if configured, an update callback will be fired.

Perform this request server side as it uses your private token. This API endpoint is limited to domestic purchases.

Response description

{
  "purchase_id": "String. The purchase id.",
  "message": "String. If Two Tap has completed the request. eg: 'done'.",
}
Suggest Edits

/purchase/history

 
gethttps://api.twotap.com/v1.0/purchase/history
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

since = '1441466540'

response = RestClient.get "https://api.twotap.com/v1.0/purchase/history?private_token=PRIVATE_TOKEN&since=#{since}"

puts response.body
<?php 

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$since = '1441466540';

$response = $api->purchase()->history($since);
A binary file was returned

You couldn't be authenticated

{  
  "message":"done",
  "purchases":[  
  {  
    "id":"5812901aa14d97051329f66a",
     "notes":null,
     "created_at":1477611548
   }
  ]
}

Body Params

private_token
string
required

Your Two Tap PRIVATE token.

since
string
required

UNIX timestamp to return a partial list of purchases.

 

This endpoint returns a list of purchase ids that your app has generated.

Use /purchase/status for detailed information.

Response description

{  
  "message": "String. If Two Tap has completed the request. eg: 'done', 'still_processing'",
  "purchases": [  
    {  
      "id": "String. The purchase id.",
      "created_at": "Integer. A timestamp containing the purchase date & time."
    }
  ]
}
Suggest Edits

/fields_input_validate

 
posthttps://api.twotap.com/v1.0/fields_input_validate
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

cart_id = ARGV[0]

# A hash like { field_key: field_value }
flat_fields_input = { 
  shipping_first_name: 'name',
  shipping_address: 'address' 
}

response = RestClient.post "https://api.twotap.com/v1.0/fields_input_validate?public_token=PUBLIC_TOKEN", { 
  cart_id: cart_id,
  flat_fields_input: flat_fields_input 
}

puts response.body
#!/bin/bash

curl "https://api.twotap.com/v1.0/fields_input_validate?public_token=$PUBLIC_TOKEN" --header 'Content-Type: application/json' --data-binary '
{
	"cart_id": "CART_ID",
  "flat_fields_input": {
	  "shipping_first_name": "name",
    "shipping_address": "address"
  }
}'
#!/usr/bin/python

import requests
import json

public_token = 'PUBLIC_TOKEN'

payload = {
  "cart_id": "CART_ID",
  "flat_fields_input": {
    "shipping_first_name": "name",
    "shipping_address": "address"
  }
}
r = requests.post('https://api.twotap.com/v1.0/fields_input_validate?public_token=' + public_token, data=json.dumps(payload), headers={'Content-Type': 'application/json'})
print(r.json())
$.post('https://api.twotap.com/v1.0/fields_input_validate?public_token=PUBLIC_TOKEN', {
  "cart_id": "CART_ID",
  "flat_fields_input": {
    "shipping_first_name": "name",
    "shipping_address": "address"
  }
}, function(data, status) {
    console.log(data);
});
var request = require("request");
var public_token = 'PUBLIC_TOKEN';

request({
  url: 'https://api.twotap.com/v1.0/fields_input_validate?public_token=' + public_token',
  json: {   
  	"cart_id": "CART_ID",
    "flat_fields_input": {
      "shipping_first_name": "name",
      "shipping_address": "address"
  },
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});var request = require("request");
request({
  url: 'https://api.twotap.com/v1.0/fields_input_validate',
  json: {"shipping_first_name": "name", "shipping_address": "address"},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$cartId = 'CART_ID';
$flatFiledsInput = [
  "shipping_first_name" => "name",
  "shipping_address" => "address"
];

$response = $api->utils()->fields_input_validate($cartId, $flatFiledsInput);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    url := "https://api.twotap.com/v1.0/fields_input_validate?public_token=" + public_token
    var jsonStr = []byte(`{
  "cart_id": "CART_ID",
  "flat_fields_input": {
    "shipping_first_name": "name",
    "shipping_address": "address"
   }
}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
A binary file was returned

You couldn't be authenticated

{
  "message": "done or bad_required_fields",
  "description": "A string that lists the issues."
}

Body Params

cart_id
string

The cart id that is sent by /cart.

public_token
string
required

Your Two Tap public token.

flat_fields_input
object
required

A hash with input values.

 

Let's say you are capturing data from the user. Two Tap requires it to be in a certain format or it will reject it. This API method allows you to validate it before sending it with the purchase.

This endpoint does not do address validation. You have to use a service like Lob or EasyPost on your end.

The 'flat_fields_input' parameter

The flat_fields_input parameter is a hash that looks like below:

{
  "shipping_first_name": "shipping first name",
  "shipping_last_name": "shipping last name",
  [ and so on ]
}

Response description

{
  "message": "String. If Two Tap has completed the request. eg: 'still_processing', 'bad_required_fields'",
  "description": "String. A more human-friendly description of the message. eg: 'Input is OK.'"
}
 
gethttps://api.twotap.com/v1.0/quicky
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'


response = RestClient.get "https://api.twotap.com/v1.0/quicky?public_token=PUBLIC_TOKEN&sms_confirm_url=SMS_CONFIRM_URL&phone=PHONE&message=MESSAGE&products=PRODUCTS"

puts response.body
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$products = ['PRODUCT_URL'];
$smsConfirmUrl = 'SMS_CONFIRM_URL';
$phone = 'PHONE';

$response = $api->utils()->quicky($products, $smsConfirmUrl, $phone);
A binary file was returned

You couldn't be authenticated

{
  "message": "done",
  "description": "Message sent to PHONE.",
  "contents": "We heard you'd like to buy some products. Tap here to start: http://ttap.co/ID."
}

Query Params

public_token
string
required

Your Two Tap public token.

products
string
required

A list of products urls separated by commas. We encourage you to encode each product url beforehand.

sms_confirm_url
string
required

Quicky defaults to the SMS flow, which requires the implementation of a confirm url.

phone
string
required

The shopper's phone number. US only for now.

message
string

(Optional) The message the shopper would receive. eg: "We heard you'd like to buy some products. Tap here to start: %%URL%%."

 

Quicky is Two Tap's version of the Amazon Dash Replenishment Service. When you hit this endpoint the shopper will receive an SMS with a link to purchase certain products.

You still have to implement the SMS Confirm URL in your backend.

Response description

{
  "message": "String. If Two Tap has completed the request. eg: 'done', 'still_processing'",
  "description": "String. A more human-friendly description of the message. eg: 'Message sent to PHONE.'",
  "contents": "String. The message sent to the shopper. eg: 'We heard you'd like to buy some products. Tap here to start: http://ttap.co/ID."
}
Suggest Edits

/supported_sites

 
gethttps://api.twotap.com/v1.0/supported_sites
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.get "https://api.twotap.com/v1.0/supported_sites"

puts response.body
#!/bin/bash

curl https://api.twotap.com/v1.0/supported_sites
#!/usr/bin/python

import requests

r = requests.get('https://api.twotap.com/v1.0/supported_sites');
print(r.json())
$.get("https://api.twotap.com/v1.0/supported_sites", function(data, status) {
  console.log(data);
})
var request = require("request");
request({
  url: 'https://api.twotap.com/v1.0/supported_sites',
  method: "GET"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$country = 'all';

$supportedSites = $api->utils()->supportedSites($country);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
    url := "https://api.twotap.com/v1.0/supported_sites"
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/supported_sites");
      var result = client.GetAsync ("").Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

[
  {
    "id": "52d7a85ece04fabdcd000005",
    "name": "Shoebuy",
    "url": "shoebuy.com",
    "logo": "https://core.twotap.com/system/recipes/logos/5374/e544/ce04/faea/d600/000f/small/Shoebuy_2.png?1400808613861",
    "country": "us",
    "creates_stores_account": false,
    "coupon_support": true,
    "gift_card_support": false,
    "feed_support": false,
    "checkout_support": [
      "noauthCheckout"
    ],
    "free_shipping_thresholds": {
      "cheapest": "$25.00"
    },
    "commission_type": "percentage",
    "commission_range": {
      "min": 5.0,
      "max": 15.0
    }
  }
  [..]
]

Body Params

country
string

Filter by country. Valid options are all, us, uk, au, etc.

private_token
string

(Optional) Your Two Tap PRIVATE token.

 

Query this endpoint to receive a list of Two Tap's currently supported sites.

By default, it returns only US stores. Set the country argument to all to receive all the integrated sites.

If you send your private token (which you should only do server-side), you will also receive information about the commission rates you have in the retailer programs. This is also helpful for getting a list of the retailer campaigns where you are approved.

Response description

[
  {
    "id": "String. The site's id.",
    "name": "String. The site's name.",
    "url": "String/URL. The site's domain.",
    "logo": "String/URL. A string with the site's image URL.",
    "country": "String. What is considered as 'domestic' shipping for this integration.",
    "creates_stores_account": "Boolean. true or false. True if the site requires accounts during noauthCheckout (guest checkout). In that case Two Tap will use intermediary accounts to place the orders.",
    "coupon_support": "Boolean. Always true.",
    "gift_card_support": "Boolean. Always true.",
    "feed_support": "Boolean. true or false. Whether this retailer's products are available inside Two Tap product catalog (/product API endpoints).",
    "checkout_support": "Array. The available checkout types. Possible values are 'noauthCheckout', 'localCheckout', and 'authCheckout'.",
    "free_shipping_thresholds": { 
       "shipping option name: 'fastest' or 'cheapest'": "String/Price. The permanent threshold when shipping becomes free. Doesn't take into consideration temporary free shipping offers.",
    },
  },
]
 
gethttps://api.twotap.com/v1.0/coupons
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.get "https://api.twotap.com/v1.0/coupons?private_token=PRIVATE_TOKEN"

puts response.body
#!/bin/bash

PRIVATE_TOKEN=PRIVATE_TOKEN

curl https://api.twotap.com/v1.0/coupons?private_token=$PRIVATE_TOKEN
#!/usr/bin/python

import requests

private_token = 'PRIVATE_TOKEN'

r = requests.get('https://api.twotap.com/v1.0/coupons?private_token=' + private_token)

print(r.json())
$.get("https://api.twotap.com/v1.0/coupons?private_token=PRIVATE_TOKEN", function(data, status) {
  console.log(data);
})
var request = require("request");
var private_token = 'PRIVATE_TOKEN';

request({
  url: 'https://api.twotap.com/v1.0/coupons?private_token=' + private_token,
  method: "GET"
}, function (err, reponse, body) {
  console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$coupons = $api->utils()->coupons();
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
    private_token := "PRIVATE_TOKEN"
    url := "https://api.twotap.com/v1.0/coupons?private_token=" + private_token
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
A binary file was returned

You couldn't be authenticated

[  
   {  
      "site_id":"53fc5564ce04fabc27000019",
      "code":"AFFSHOP1",
      "label":"15% off any order.",
      "rules":[  
         {  
            "free_shipping":false,
            "sale":false,
            "one_item":false,
            "percentage_off":15
         }
      ],
      "starts_at":"2014-04-01T07:00:00.000Z",
      "ends_at":"2017-04-03T14:59:00.000Z"
   },
   {  
      "site_id":"54817d1a69702d46e7100000",
      "code":"AFFREG",
      "label":"$7 off $70+ order.",
      "rules":[  
         {  
            "free_shipping":false,
            "sale":false,
            "one_item":false,
            "value_off":7,
            "threshold":70
         }
      ],
      "starts_at":"2015-02-06T08:00:00.000Z",
      "ends_at":"2051-01-01T07:59:00.000Z"
   },
   {  
      "site_id":"5643271869702d6af5000001",
      "code":"CJ75",
      "label":"5% off $75+ order.",
      "rules":[  
         {  
            "free_shipping":false,
            "sale":false,
            "one_item":false,
            "percentage_off":5,
            "threshold":75
         }
      ],
      "starts_at":"2008-11-13T08:00:00.000Z",
      "ends_at":"2051-01-01T07:59:59.000Z"
   },
   {  
      "site_id":"5576f02969702d6705080000",
      "code":"TGSHOP15",
      "label":"15% off any order.",
      "rules":[  
         {  
            "free_shipping":false,
            "sale":false,
            "one_item":false,
            "percentage_off":15
         }
      ],
      "starts_at":"2016-04-05T07:00:00.000Z",
      "ends_at":"2025-12-31T08:00:00.000Z"
   }
]

Body Params

private_token
string
required

Your Two Tap PRIVATE token.

 

While Two Tap applies any coupon you send during /purchase it also has a database of active coupons.

The database is used at /cart/estimates in order to, well, estimate the coupon discount before actually starting the checkout on the retailer site.

This database is manually curated from different sources and is NOT automated. It only contains /cart/estimates compatible coupons, which means site-wide discounts. Product/category coupons are currently not supported.

Our team is happy to accept new coupons and enter them in our database. Just email support@twotap.com.

There's no associated cost with this endpoint, all coupons are offered for free.

Want to report a missing coupon?

If you know about a coupon that's not listed here please email coupons@twotap.com with:

  • coupon name, coupon label (eg. 'free shipping above $50'), coupon start date, coupon end date.

Coupons that are site-wide will be added in 12 hours at the most.

Because this request requires a private token you should only perform it server side.

How to parse the coupon rules.

Coupons are split into benefits and conditions. If the conditions are met you can apply the benefits. Multiple rules can be sent for one coupon in complex situations (5% off $100+, 10% off $200).

Benefits

free_shipping

(true/false) Free shipping

percentage_off

(float) Discount is a % off the subtotal

value_off

(float) Discount is a fixed amount from the subtotal. Uses the site's CURRENCY_FORMAT.

Conditions

threshold

(float) The subtotal has to be above this value.
Uses the site's CURRENCY_FORMAT

sale

(true/false) If this discount applies to only products on sale.

one_item

(true/false) If this discount applies to only one item from the cart.

Response description

[
  {
    "site_id": "String. The site's id.",
    "code": "String. The coupon code.",
    "label": "String. A description of the code.",
    "rules": [
      {
        "[type of benefit eg:'percentage_off', 'value_of']": "Integer. The percent or value of the discount.",
      }
    ],
    "starts_at": "Integer. Timestamp when the coupon starts.",
    "ends_at": "Integer. Timestamp when the coupon ends."
  }
]
Suggest Edits

Pickup from Store

 

Two Tap has BETA support for ordering a product and picking up in store.

Pickup from store is enabled on demand. If you'd like us to support it for a retailer please email us at support@twotap.com.

Limitations

There are a couple of limitations:

  • It requires retailers to support pickup from store on their website.
  • It only works via the API, there is no Cart support at this time.
  • Products that are only pickup from store (no shipping support) can not be ordered at this time.
  • All products in a /cart need to be available for pickup.

We are working to extend this functionality in the near future.

Suggest Edits

Before /pickup_options

 

As mentioned above, all products in a /cart need to be able to be picked up from store. You should check that pickup_support is true in the /cart/status response for all products.

Suggest Edits

/pickup_options

 
posthttps://api.twotap.com/v1.0/pickup_options
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

cart_id = ARGV[0]

fields_input = { 
  [site_id]: {
    "localCheckout" => { "shipping_zip" => "94303" },
    "addToCart": {
      [product_md5]: { "quantity": 1 }
    }
  }
}

products = [ 'http://fab.com/sale/4850/product/11263' ]

response = RestClient.post "https://api.twotap.com/v1.0/pickup_options?public_token=PUBLIC_TOKEN", { 
  cart_id: cart_id, 
  fields_input: fields_input,
  products: products 
}

puts response.body
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$cartId = 'CART_ID';
$fieldsInput = [
  "[site_id]" => [
    "localCheckout" => [
      "shipping_zip" => "94303"
    ],
    "addToCart" => [
      "[product_md5]" => [
        "quantity" => 1
      ]
    ]
  ]
];

$response = $api->pickupOptions()->create($cartId, $fieldsInput);
A binary file was returned

You couldn't be authenticated

{
  "purchase_id": "50f414b9e6a4869bf6000010",
  "message": "still_processing",
  "description": "Still processing."
}

Body Params

public_token
string
required

Your Two Tap public token.

cart_id
string
required

The cart id that is sent by /cart.

fields_input
object
required

A hash containing pickup information by site_id.

products
string

(Optional) An array of product URLs that have accepted by the user. This is useful in case an user removes some items between /cart and /purchase. If specified fields_input should contain only the required data for those products. These are Two Tap urls from /cart/status, and NOT the original_urls that you originally send us.

finished_url
string

Optional) An endpoint where Two Tap can POST the pickup information once it has been retrieved.

 

Pickup Options will begin a checkout session on the retailer's end and will stop as soon as it can retrieve the available pickup stores.

This request needs to be POSTed with a shipping_zip for each site_id.

This request is processed in the background which means you have to call /pickup_options/status.

The 'fields_input' parameter

The fields_input parameter is a hash that looks like below:

"5225da1055a0f96c8b000004" => {
  "localCheckout" => { "shipping_zip" => "94303" },
  "addToCart" => {
    "82efa6ab18e09ae30893e2aa8b74b0a1" => { "quantity" => 1 },
  }
}

Response description

{
  "purchase_id": "String. The purchase id.",
  "message": "String. If Two Tap has completed the request. eg: 'done', 'still_processing'",
  "description": "String. A more human-friendly description of the message. eg: 'Still processing.'"
}
Suggest Edits

/pickup_options/status

 
gethttps://api.twotap.com/v1.0/pickup_options/status
#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.get "https://api.twotap.com/v1.0/pickup_options/status?public_token=PUBLIC_TOKEN&cart_id=#{cart_id}"

puts response.body
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$cartId = 'CART_ID';

$response = $api->pickupOptions()->status($cartId);
A binary file was returned

You couldn't be authenticated

{
  "cart_id": "56131fe1b88a91ba1a9b0d7a",
  "message": "still_processing",
  "description": "Still processing.",
  "sites": {
    "532603efe6a4867999000001": {
      "last_message": "Adding product to cart",
      "percent_done": 33.53819139596137,
      "status": "still_processing"
    }
  }
}
{
  "sites":{
    "532603efe6a4867999000001":{
      "pickup_options":{
        "13edbb4cf92aa22f5d454c653093945a":[
          {
            "when":"OCT 14 - OCT 19",
            "phone":"",
            "name":"Kifer Rd #640",
            "distance":"3.70 miles away",
            "address":"680 Kifer Rd, Sunnyvale, CA 94086 | (408)245-3686"
          },
          {
            "when":"OCT 14 - OCT 19",
            "phone":"",
            "name":"Bollinger #6635",
            "distance":"5.58 miles away",
            "address":"975 De Anza Blvd, San Jose, CA 95129 | (408)253-3537"
          }
        ]
      },
      "status":"done"
    }
  },
  "cart_id":"56131fe1b88a91ba1a9b0d7a",
  "message":"done"
}

Query Params

public_token
string
required

Your Two Tap public token.

cart_id
string
required

The cart's id.

 

Response description

{
  "sites": {
    "[site_id]": {
      "pickup_options": {
        "[product_md5]": [
          {
            "when": "String. Optional. When the product can be picked up at this store.",
            "phone": "String. Optional. The pickup store's phone number.",
            "name": "String. The pickup store's name.",
            "distance": "String. Optional. Distance to the store.",
            "address": "String. Optional. Store address."
          },
        ]
      },
      "status": "String. The status, which can be 'still_processing', 'has_failures', or 'done'."
    }
  },
  "cart_id": "String. The cart's id.",
  "message": "String. If Two Tap has completed the request: 'still_processing', 'has_failures', or 'done'."
}
Suggest Edits

After /pickup_options

 

At the end of /pickup_options/status you receive an array of stores. Send the store's name in the pickup hash as shown below, and use localCheckout.

For more information see the /purchase API method.

#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'
require 'json'

cart_id = ARGV[0]

required_fields = { 
  "5225da1055a0f96c8b000004" => {
    "localCheckout" => { ... },
    "addToCart" => {
      "82efa6ab18e09ae30893e2aa8b74b0a1" => { "quantity" => 1 },
    },
    "pickup" => {
      "82efa6ab18e09ae30893e2aa8b74b0a1" => "__STORE_NAME__",
    }
  }
}

response = RestClient.post "https://api.twotap.com/v1.0/purchase?public_token=9aad34ed6833a78b268f572a2cb182", { :cart_id => cart_id, :fields_input => required_fields }

puts response.body
Suggest Edits

The Wallet

 

The wallet allows users and publishers to store data with Two Tap and use them for purchases. The retrieving and storing of data is integrated into the /cart and /purchase requests if user_token is sent as a parameter.

There are three ways of enabling a wallet inside your app.

Shared wallet

With the shared wallet, apps agree that if a shopper enters her information in one app it should be available in any other app that implements the shared wallet. This is similar to how Visa Checkout or Masterpass works.

The shared wallet requires a two-factor SMS flow on the consumer side.

In order to implement the shared wallet you have to contact us.

Private wallet

Some publishers prefer to have their own private wallet and avoid the shared one because of the SMS authentication. Information about this integration type is available below under /wallet/user_token.

Advanced private wallet

If you're looking for a more advanced wallet that allows you to connect to both Two Tap and payment processors at the same time Two Tap has partnered with Spreedly.

Suggest Edits

/wallet/user_token

 
posthttps://api.twotap.com/v1.0/wallet/user_token
response = RestClient.post "https://api.twotap.com/v1.0/wallet/user_token?private_token=PRIVATE_TOKEN", { 
  user_key: "user@user.com",
}
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$userKey = 'USER_KEY';

$response = $api->wallet()->userToken($userKey);
A binary file was returned

You couldn't be authenticated

{ 
  "message": "done",
  "user_token": "p_ZPSMDvaWDrHl2BoAfAqMveCAIImZfalx"
}

Body Params

private_token
string
required

Your Two Tap PRIVATE token.

user_key
string
required

A key to identify the user by. Usually this is the shopper's email address.

 

This method allows you to get a user_token for Two Tap's private wallet. Send a user_key, which is usually the email address, and Two Tap will send back a user_token associated with that user_key that you can use with the other /wallet endpoints.

Since this request requires a private token make sure you call it server-side.

Suggest Edits

/wallet/store

 
posthttps://api.twotap.com/v1.0/wallet/store
response = RestClient.post "https://api.twotap.com/v1.0/wallet/store?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN", { 
  field_type: "shipping",
  group_id: "xvv4pldi (optional, only if you are updating an existing group_id)",
  fields: { 
    shipping_first_name: "My new first name",
    shipping_last_name: "My new last name"
  }
}
#!/bin/bash

PUBLIC_TOKEN=PUBLIC_TOKEN

curl https://api.twotap.com/v1.0/wallet/store?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN --header 'Content-Type: application/json' --data-binary '{ 
  "field_type": "shipping",
  "group_id": "xvv4pldi (optional)",
  "fields": { 
    "shipping_first_name": "My new first name",
    "shipping_last_name": "My new last name"
  }
}'
#!/usr/bin/python

import requests
import json

payload = { 
  "field_type": "shipping",
  "group_id": "xvv4pldi (optional)",
  "fields": { 
    "shipping_first_name": "My new first name",
    "shipping_last_name": "My new last name"
  }
}

r = requests.post('https://api.twotap.com/v1.0/wallet/store?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN',
  data=json.dumps(payload), headers={'Content-Type': 'application/json'})

print(r.json())
$.post('https://api.twotap.com/v1.0/wallet/store?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN', {
  "field_type": "shipping",
  "group_id": "xvv4pldi (optional)",
  "fields": { 
    "shipping_first_name": "My new first name",
    "shipping_last_name": "My new last name"
  }
}, function(data, status) {
    console.log(data);
});
var request = require("request");
request({
    url: 'https://api.twotap.com/v1.0/wallet/store?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN'
    json: { 
    "field_type": "shipping",
    "group_id": "xvv4pldi (optional)",
    "fields": { 
      "shipping_first_name": "My new first name",
      "shipping_last_name": "My new last name"
    }
  },
    method: "POST"
}, function (err, reponse, body) {
    console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$userToken = 'USER_TOKEN';
$fieldType = 'shipping';
$groupId = 'xvv4pldi (optional)';
$fields = [
  "shipping_first_name" => "My new first name",
  "shipping_last_name" => "My new last name"
];

$response = $api->wallet()->store($userToken, $fieldType, $groupId, $fields);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    url := "https://api.twotap.com/v1.0/wallet/store?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN"
    var jsonStr = []byte(`{ 
      "field_type": "shipping",
      "group_id:" "xvv4pldi (optional)",
      "fields": { 
        "shipping_first_name": "My new first name",
        "shipping_last_name": "My new last name"
      }
    }`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/wallet/store?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN");
      dynamic payload = new ExpandoObject ();
      payload.field_type = "shipping";
      payload.group_id = "xvv4pldi (optional)";
      payload.fields = new ExpandoObject ();
      payload.fields.shipping_first_name = "My new first name";
      payload.shipping_last_name = "My new last name";
      string json = JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json, Encoding.UTF8, "application/json")).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{ 
  "message": "done",
  "stored_field_values": {
    "xvv4pldi": {
      "data": {
        "email": {
          "text": "anemail@gmail.com"
        },
        "shipping_last_name": {
          "text": "My new last name"
        },
        "shipping_first_name": {
          "text": "My new first name"
        }
      },
      "name": "My new first name, My new last name, anemail@gmail.com"
    }
  }
}

Body Params

public_token
string
required

Your Two Tap public token.

user_token
string
required

The shared or private user_token.

fields
object
required

The hash contains the information to store in the wallet. Only strings are accepted as the object values.

field_type
string
required

shipping or payment

group_id
string

(Optional) If you want to update an existing group.

 

To create a new entry only send a field_type and a list of fields. Two Tap will automatically generate a group_id.

If you'd like to update an existing group also send a specific group_id.

Keep in mind that during /purchase Two Tap by default automatically stores the information inside fields_input when user_token is sent.

If you are using this endpoint you might want to set store_in_wallet to false during /purchase.

Suggest Edits

/wallet/retrieve

 
posthttps://api.twotap.com/v1.0/wallet/retrieve
response = RestClient.post "https://api.twotap.com/v1.0/wallet/retrieve?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN", { 
  filter_field_types: [ "shipping" ],
  group_id: [ "xvv4pldi" ]
}
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$userToken = 'USER_TOKEN';

$response = $api->wallet()->retrieve($userToken);
A binary file was returned

You couldn't be authenticated

{
  "message": "done",
  "stored_field_values": {
    "xvv4pldi": {
      "data": {
        "email": {
          "text": "anemail@gmail.com"
        },
        "shipping_last_name": {
          "text": "My new last name"
        },
        "shipping_first_name": {
          "text": "My new first name"
        }
      },
      "name": "My new first name, My new last name, anemail@gmail.com"
    }
  }
}

Body Params

public_token
string
required

Your Two Tap public token.

user_token
string
required

The shared or private user_token.

filter_field_types
array of strings

(Optional) Limit to shipping or payment, or both.

filter_group_ids
array of strings

(Optional) Only return certain group ids.

 

You can also retrieve data independently of /cart.

Suggest Edits

Using data (/purchase)

 

Each shipping, payment, and login groups have an id associated with it. These fields are used during /purchase.

Instead of adding the key=value pairs to the required field values just use shipping_group_id, payment_group_id, or login_group_id with the associated value.

Wallet Usage

// During a /purchase request.
required_fields = { 
  "516dece5e6a4862049000001": {
    "noauthCheckout": { 
      "shipping_group_id": "xvv4pldi",  
      "payment_group_id": "frx2d9d", 
      "an_extra_required_field": "has this value" }, 
    "addToCart": {
      "6e69ada944a4c35bb320615b827ad60a": { 
        "quantity": 1, 
        "Size": "13233550|13442751", "Color": 
        "13233550|1055745" }
    }
  }
}
Suggest Edits

/wallet/delete

 
posthttps://api.twotap.com/v1.0/wallet/delete
response = RestClient.post "https://api.twotap.com/v1.0/wallet/delete?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN", { 
  field_type: "shipping",
  group_id: "xvv4pldi"
}
#!/bin/bash

curl https://api.twotap.com/v1.0/wallet/delete?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN --header 'Content-Type: application/json' --data-binary '{
  "field_type": "shipping",
  "group_id": "xvv4pldi"
}'
#!/usr/bin/python

import requests
import json

payload = { 
  "field_type": "shipping",
  "group_id": "xvv4pldi"
}

r = requests.post('https://api.twotap.com/v1.0/wallet/delete?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN',
  data=json.dumps(payload), headers={'Content-Type': 'application/json'})
print(r.json())
$.post('https://api.twotap.com/v1.0/wallet/delete?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN', {
  "field_type": "shipping",
  "group_id": "xvv4pldi"
}, function(data, status) {
    console.log(data);
});
var request = require("request");
request({
  url: 'https://api.twotap.com/v1.0/wallet/delete?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN'
    json: { 
      "field_type": "shipping",
      "group_id": "xvv4pldi"
    },
    method: "POST"
}, function (err, reponse, body) {
    console.log(body); 
});
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$userToken = 'USER_TOKEN';
$fieldType = 'shipping';
$groupId = 'xvv4pldi (optional)';

$response = $api->wallet()->delete($userToken, $fieldType, $groupId);
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    url := "https://api.twotap.com/v1.0/wallet/delete?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN"
    var jsonStr = []byte(`{ 
      "field_type": "shipping",
      "group_id": "xvv4pldi"
    }`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/wallet/delete?public_token=PUBLIC_TOKEN&user_token=USER_TOKEN");
      dynamic payload = new ExpandoObject ();
      payload.field_type = "shipping";
      payload.group_id = "xvv4pldi";
      string json = JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json, Encoding.UTF8, "application/json")).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
A binary file was returned

You couldn't be authenticated

{ 
  "message": "done", 
  "group_id": "xvv4pldi" 
}

Body Params

public_token
string
required

Your Two Tap public token.

user_token
string
required

The shared or private user_token.

field_type
string
required

shipping or payment

group_id
string
required

The group id you want to delete.

 

To delete an entry from the wallet all you have to do is send a field_type and a group_id.

Suggest Edits

/wallet/meta

 
posthttps://api.twotap.com/v1.0/wallet/meta
#!/usr/bin/env ruby

require 'rubygems'
require 'httparty'

purchase_id = ARGV[0]

field_type = 'payment'
expires_in = 3600

meta_fields = { 
  'card_type' => 'Visa',
  'card_name' => 'John Carter',
  'card_number' => '4111111111111111',
  'expiry_date_month' => '03',
  'expiry_date_year' => '2017',
  'billing_first_name' => 'John',
  'billing_last_name' => 'Carter',
  'billing_address' => 'An address',
  'billing_city' => 'Palo Alto',
  'billing_state' => 'California',
  'billing_zip' => '94103',
  'billing_country' => 'United States of America',
  'billing_telephone' => '6501231234'
 }


response = HTTParty.post "https://api.twotap.com/v1.0/wallet/meta?public_token=PUBLIC_TOKEN", :body => { 
  'meta_fields' => meta_fields,
  'field_type' => 'payment',
  'expires_in' => expires_in 
 }

puts response.body
<?php

require 'vendor/autoload.php';

use TwoTap/Api;

$api = new Api();

$metaFields = [
  'card_type' => 'Visa',
  'card_name' => 'John Carter',
  'card_number' => '4111111111111111',
  'expiry_date_month' => '03',
  'expiry_date_year' => '2017',
  'billing_first_name' => 'John',
  'billing_last_name' => 'Carter',
  'billing_address' => 'An address',
  'billing_city' => 'Palo Alto',
  'billing_state' => 'California',
  'billing_zip' => '94103',
  'billing_country' => 'United States of America',
  'billing_telephone' => '6501231234'
];
$fieldType = 'payment';
$expiresIn = 3600;

$response = $api->wallet()->meta($metaFields, $fieldType, $expiresIn);
A binary file was returned

You couldn't be authenticated

{
  "message": "done",
  "meta_id": "Tn0SHOSpekKxTcQ3CLbq"
}

Body Params

public_token
string
required

Your Two Tap public token.

meta_fields
object
required

An hash containing the meta information

field_type
string
required

Set it to payment.

expires_in
int32
required

When this information should expire, in seconds.

 

When using the Cart there are cases where you'd be interested in hiding some data from the consumer.

The most common use case is when you are interested in prefilling payment data inside the Cart interface, but you'd rather keep a [Your Server] <-> Two Tap communication channel for PCI DSS reasons.

Meta allows you to do that.

Using Meta

When sending input_fields with the Cart send the meta_id you receive from this endpoint like so:

"input_fields": {
  "payment_meta_id": "meta_id from this endpoint",
  "payment_meta_description": "eg Visa ending in 1111",
  "any_other_field": "field value"
}