この記事でできること
Shopify Flowから直接Ship&co APIを呼び出して、ギフト注文の送り状を自動作成します。
ギフト注文では、こんな要望がよくあります。
- 送り主(依頼主)= 請求先住所(購入した本人)
- お届け先 = 配送先住所(プレゼントを受け取る人)
Shopify FlowとShip&co APIを組み合わせれば、この振り分けを自動化できます。ノーコードツールだけでは対応が難しい部分を、Liquidテンプレートで補います。
🧪 Postmanコレクション(リクエスト例):https://postman.shipandco.com
📖 Ship&co APIドキュメント:https://developer.shipandco.com
全体の構成:ワークフローを2つに分ける
Shopify Flowのワークフローは、決まったトリガー(「注文作成時」「注文支払い時」「注文タグ追加時」など)からしか開始できません。そのため、ロジックを2つのワークフローに分けます。
ワークフローA:ギフト注文を検出してタグを付ける
| 項目 | 設定 |
|---|---|
| トリガー | 注文作成時(Order created)または 注文支払い時(Order paid) |
| 条件 | ギフト判定(特定商品の有無、order.customAttributes(注文属性 / note attributes)、order.note(注文メモ)など) |
| アクション | 注文タグを追加 → gift
|
ワークフローB:ギフト注文だけShip&co APIを呼ぶ
| 項目 | 設定 |
|---|---|
| トリガー | 注文タグ追加時(Order tagged) |
| 条件 | 追加されたタグが gift
|
| アクション | Send HTTP request → Ship&co APIで送り状作成 |
| 後続処理 | ステータスが200なら gift-label-created タグを追加 |
なぜ2つに分けるのか?
「注文作成時」のトリガー内でタグを追加しても、同じワークフロー内では後続の条件分岐に反映されません。タグ追加をトリガーにした別ワークフローを用意することで、確実にギフト注文だけをAPI連携に流せます。
Flowの「Send HTTP request」設定
| 項目 | 値 |
|---|---|
| HTTP method | POST |
| URL | https://api.shipandco.com/v1/shipments |
| Header 1 | Content-Type: application/json |
| Header 2 | x-access-token: {{secrets.x-access-token}} |
APIトークンはシークレットとして保存する
{{secrets.x-access-token}} の部分は、Shopifyのシークレット機能を使っています。
Shopifyでは、APIトークンなどの機密情報をストア設定側に保存し、ワークフローからは変数として参照できます。これには重要なメリットがあります。
- ワークフローの画面上にトークンの値が表示されない
- ストアのFlow管理画面にアクセスできるスタッフでも、トークンの中身を見ることができない
- トークンを更新したいときは、ワークフローを編集せずに設定側だけ差し替えられる
Flowの「Send HTTP request」アクションで 「Add secret」 をクリックし、キー名(例:x-access-token)と値を登録してください。以降は {{secrets.キー名}} で参照できます。
Ship&coのAPIトークンは、Ship&coアプリの /api ページから発行できます。
つまずきポイント:日本の住所形式
ここが一番時間のかかった部分です。Shopifyの住所データは、そのままでは日本の運送会社に渡せません。
| 項目 | Shopifyが返す形式 | ヤマト運輸・佐川急便・Ship&co APIが求める形式 |
|---|---|---|
| 都道府県 |
provinceCode = JP-13 のようなコード |
東京都 のような漢字表記
|
| 市区町村 |
city フィールドに分離 |
address1 に市区町村+番地をまとめて格納 |
なお、電話番号や郵便番号のハイフンは、Ship&co APIはどちらの形式でも受け付けます(150-0041 でも 1500001 でもOK)。この記事のサンプルではハイフンを除去していますが、必須の処理ではありません。
解決策1:都道府県コードを漢字に変換する
Liquidの配列を使って、JP-13 → 東京都 に変換します。
{% assign jp_prefs = "北海道,青森県,岩手県,宮城県,秋田県,山形県,福島県,茨城県,栃木県,群馬県,埼玉県,千葉県,東京都,神奈川県,新潟県,富山県,石川県,福井県,山梨県,長野県,岐阜県,静岡県,愛知県,三重県,滋賀県,京都府,大阪府,兵庫県,奈良県,和歌山県,鳥取県,島根県,岡山県,広島県,山口県,徳島県,香川県,愛媛県,高知県,福岡県,佐賀県,長崎県,熊本県,大分県,宮崎県,鹿児島県,沖縄県" | split: "," %}
{% assign ship_idx = order.shippingAddress.provinceCode | remove: "JP-" | plus: 0 | minus: 1 %}
{% assign ship_prov = jp_prefs[ship_idx] %}
JP- を除去して数値化し、配列のインデックス(0始まりなので -1)で都道府県名を取得します。JISコードの順番と配列の順番が一致するため、この方法で正確に変換できます。
解決策2:市区町村を address1 に結合する
"address1": "{{ order.shippingAddress.city }}{{ order.shippingAddress.address1 }}"
これで「渋谷区神南1-2-3」のような日本式の住所文字列になります。
(任意)ハイフンや余分なスペースを除去する
必須ではありませんが、データを揃えておきたい場合は remove フィルタで整形できます。
"phone": "{{ order.shippingAddress.phone | remove: '-' | remove: ' ' }}",
"zip": "{{ order.shippingAddress.zip | remove: '-' }}"
つまずきポイント:ヤマト運輸は発送日が必須
ヤマト運輸で送り状を作成する場合、shipment_date の指定が必要です。注文日をそのまま使うのが簡単です。
"shipment_date": "{{ order.createdAt | date: '%Y-%m-%d' }}"
翌日発送にしたい場合(注文日 +1日)
一度Unixタイムスタンプに変換してから86400秒(1日)を加算し、再度日付形式に戻します。
"shipment_date": "{{ order.createdAt | date: '%s' | plus: 86400 | date: '%Y-%m-%d' }}"
2日後にしたい場合は plus: 172800(86400 × 2)に変更してください。
ヤマト運輸の場合、発行された送り状の使用期限は30日以内です。
商品情報:ギフトの中身を隠すこともできる
ギフト注文では、受取人に商品名を知られたくないケースがあります。products の name は自由な文字列を指定できるため、実際の商品名を出さずに「ギフト ワイン」のような表記に置き換えられます。
"products": [
{ "name": "ギフト ワイン", "quantity": 1, "price": 0 }
]
実際の商品名を使いたい場合は、order.lineItems をループします。
"products": [
{% for lineItem in order.lineItems %}{
"name": "{{ lineItem.name }}",
"price": {{ lineItem.originalUnitPriceSet.shopMoney.amount }},
"quantity": {{ lineItem.quantity }}
}{% unless forloop.last %},{% endunless %}{% endfor %}
]
💡 国内の送り状に商品価格は印字されません。
priceとして設定する値が実際の販売価格と異なっていても、送り状の印字や配送には影響しません。
ループを使う場合は{% unless forloop.last %},{% endunless %}でカンマ制御を忘れないようにしてください(JSONが不正になり400エラーになります)。
完成したリクエストボディ
Flowの「Body」欄にそのまま貼り付けられる形です。
{% assign jp_prefs = "北海道,青森県,岩手県,宮城県,秋田県,山形県,福島県,茨城県,栃木県,群馬県,埼玉県,千葉県,東京都,神奈川県,新潟県,富山県,石川県,福井県,山梨県,長野県,岐阜県,静岡県,愛知県,三重県,滋賀県,京都府,大阪府,兵庫県,奈良県,和歌山県,鳥取県,島根県,岡山県,広島県,山口県,徳島県,香川県,愛媛県,高知県,福岡県,佐賀県,長崎県,熊本県,大分県,宮崎県,鹿児島県,沖縄県" | split: "," %}
{% assign ship_idx = order.shippingAddress.provinceCode | remove: "JP-" | plus: 0 | minus: 1 %}
{% assign bill_idx = order.billingAddress.provinceCode | remove: "JP-" | plus: 0 | minus: 1 %}
{% assign ship_prov = jp_prefs[ship_idx] %}
{% assign bill_prov = jp_prefs[bill_idx] %}
{
"setup": {
"carrier": "yamato",
"service": "yamato_regular",
"shipment_date": "{{ order.createdAt | date: '%Y-%m-%d' }}",
"currency": "JPY",
"ref_number": "{{ order.name }}-gift",
"test": true
},
"to_address": {
"full_name": "{{ order.shippingAddress.name }}",{% if order.shippingAddress.company != blank %}
"company": "{{ order.shippingAddress.company }}",{% endif %}
"email": "{{ order.email }}",
"phone": "{{ order.shippingAddress.phone | remove: '-' | remove: ' ' }}",
"country": "JP",
"zip": "{{ order.shippingAddress.zip | remove: '-' }}",
"province": "{% if ship_prov != blank %}{{ ship_prov }}{% else %}{{ order.shippingAddress.province }}{% endif %}",
"address1": "{{ order.shippingAddress.city }}{{ order.shippingAddress.address1 }}"{% if order.shippingAddress.address2 != blank %},
"address2": "{{ order.shippingAddress.address2 }}"{% endif %}
},
"from_address": {
"full_name": "{{ order.billingAddress.name }}",{% if order.billingAddress.company != blank %}
"company": "{{ order.billingAddress.company }}",{% endif %}
"email": "{{ order.email }}",
"phone": "{% if order.billingAddress.phone != blank %}{{ order.billingAddress.phone | remove: '-' | remove: ' ' }}{% else %}0751234567{% endif %}",
"country": "JP",
"zip": "{{ order.billingAddress.zip | remove: '-' }}",
"province": "{% if bill_prov != blank %}{{ bill_prov }}{% else %}{{ order.billingAddress.province }}{% endif %}",
"address1": "{{ order.billingAddress.city }}{{ order.billingAddress.address1 }}"{% if order.billingAddress.address2 != blank %},
"address2": "{{ order.billingAddress.address2 }}"{% endif %}
},
"products": [
{% for lineItem in order.lineItems %}{
"name": "{{ lineItem.name }}",
"price": {{ lineItem.originalUnitPriceSet.shopMoney.amount }},
"quantity": {{ lineItem.quantity }}
}{% unless forloop.last %},{% endunless %}{% endfor %}
]
}
ポイント:
-
from_addressに 請求先住所(billingAddress)を使用 → 送り主が購入者本人になります -
to_addressに 配送先住所(shippingAddress)を使用 → お届け先がプレゼントの受取人になります - 請求先の電話番号が空の場合はフォールバック値を入れています(ほとんどの運送会社では電話番号が必須のため)
-
"test": trueでテストラベルを発行できます。本番運用時は省略してください
開発を効率化するコツ:AIエージェントを2つ使う
このワークフローを組むうえで一番役立ったのが、Shopify Dev サイトのAIエージェントと、手元のAIエージェント(Claude、Cursor、Geminiなど)を併用する方法です。
- Shopify Dev サイトのAIエージェントに「Flowで注文の請求先住所と配送先住所を取得したい」と聞く
- 返ってきたLiquid変数名やスキーマ情報を、手元のAIエージェントにコピーする
- 「これをShip&co APIのリクエスト形式に変換してほしい」と依頼する
- Ship&coのPostmanコレクションのテンプレートも一緒に渡すと精度が上がります
Shopify側のスキーマは正確な情報が必要で、Ship&co側は正確なリクエスト形式が必要です。それぞれ得意な方に聞いて、間を繋ぐのが早いです。
エラーハンドリング
Flowの「Send HTTP request」アクションでは、4XX・5XXエラー時の挙動を設定できます。
| 設定 | 推奨 |
|---|---|
| On client error (4XX) |
Fail(リクエスト内容の問題なので気づけるようにする) |
| On server error (5XX or 429) |
Fail または リトライ |
また、レスポンスが200のときだけ gift-label-created タグを追加する条件分岐を入れておくと、どの注文が処理済みかShopify管理画面から一目で分かります。
よくある質問
Q. これで完璧に動きますか?
いいえ、完璧ではありません。特に住所が長い場合の扱いは運送会社ごとに文字数制限が異なるため、追加の処理が必要になることがあります。まずはテストモードで実際の注文データを流してみることをおすすめします。
Q. 発行された送り状はどこで印刷しますか?
APIコールが成功したら、Ship&coアプリの「発行完了」ページに送り状が作成されています。そこからPDFを印刷してください。
Q. 運送会社を条件によって切り替えられますか?
はい。Flowの条件分岐とLiquidの if を使えば、こんな分岐が可能です。
- 重量が1kg以上ならヤマト運輸、それ以下ならゆうパケット
- 沖縄・北海道宛は佐川急便
- 海外宛はDHLまたはFedEx
Q. ギフト注文以外にも応用できますか?
できます。タグベースのトリガーは汎用性が高いため、「予約商品」「冷蔵便対象商品」「B2B注文」など、条件ごとに異なる運送会社・サービスを割り当てる運用にも展開できます。
まとめ
| ポイント | 内容 |
|---|---|
| ワークフロー構成 | タグ付け用とAPI呼び出し用の2本に分ける |
| 都道府県 |
provinceCode をLiquid配列で漢字に変換 |
| 市区町村 |
city + address1 を結合 |
| ヤマト運輸 |
shipment_date が必須 |
| 商品名 | 固定文字列にすればギフトの中身を隠せる(価格は送り状に印字されない) |
| APIトークン | Shopifyのシークレット機能に登録(誰にも値が見えない) |
Shopify FlowとShip&co APIの組み合わせは、標準アプリでは対応しきれない出荷ロジックを、コードを書かずに(正確には少しのLiquidだけで)実現できます。ギフト注文はその一例で、応用の幅はかなり広いです。
Ship&co APIを試してみる
→ アカウント登録(30日間無料トライアル):https://app.shipandco.com/join
→ APIドキュメント:https://developer.shipandco.com
→ Postmanコレクション:https://postman.shipandco.com
→ ユースケース紹介:https://www.shipandco.com/ja/api#cases

