業務の隙間を埋める技術メモ。

「それ、作れるか?」より 「それ、作って大丈夫か?」を考えたい。 業務で“ちゃんと使える”かどうかを、 実際に手を動かして確かめたログを残しています。

NetSuiteからkintone APIを呼び出したら401エラーが出続けた話——原因はOAuthトークンの設定ミスだった

NetSuite の SuiteScript(RESTlet)からkintone APIを叩こうとしたら、401エラーが出て全然つながらない。そんな経験はありませんか? 私はこれで数時間溶かしました。原因と解決策をそのまま書き残しておきます。


やろうとしていたこと

NetSuiteのScheduled ScriptからkintoneのREST APIを呼び出して、データを同期する仕組みを作ろうとしていました。

構成はシンプルで、こういうイメージです。

NetSuite(Scheduled Script)
  ↓ HTTP リクエスト
kintone REST API
  ↓ レスポンス
NetSuite(データ処理)

kintone側のAPIドキュメントを見ながらコードを書いて、いざ実行してみると——

{"code":"CB_AU01","message":"X-Cybozu-API-Token は不正です。"}

HTTPステータスは 401。認証エラーです。


最初に疑ったこと(全部ハズレだった)

エラーメッセージを見て、最初はこんなことを疑いました。

  • APIトークンの文字列をコピーミスしているのでは?
  • kintone側でIPアドレス制限がかかっているのでは?
  • NetSuiteからの外部HTTP通信がブロックされているのでは?

一つひとつ確認しましたが、どれも問題なし。それでも401が出続けます。


原因:OAuthトークンのヘッダー設定が間違っていた

数時間格闘した末に判明した原因は、リクエストヘッダーへのトークンのセット方法が間違っていたことでした。

kintone REST APIの認証には、ヘッダーに以下のように指定する必要があります。

X-Cybozu-API-Token: {APIトークン}

私のコードはこうなっていました。

// ❌ 間違い:Authorizationヘッダーに入れていた
var headers = {
  'Authorization': 'Bearer ' + API_TOKEN,
  'Content-Type': 'application/json'
};

kintoneはBearerトークン方式ではなく、専用ヘッダー X-Cybozu-API-Token にトークンをそのまま渡す方式です。他のAPIに慣れていると、つい Authorization: Bearer で書いてしまいます。これが原因でした。

正しいコードはこうです。

// ✅ 正しい:X-Cybozu-API-Tokenヘッダーに入れる
var headers = {
  'X-Cybozu-API-Token': API_TOKEN,
  'Content-Type': 'application/json'
};

SuiteScriptでの実装例(動作確認済み)

NetSuiteのSuiteScript 2.1で実際に動いたコードをそのまま載せておきます。

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 */
define(['N/https', 'N/log'], function(https, log) {

  function execute(context) {

    var KINTONE_DOMAIN = 'your-domain.cybozu.com'; // ← 自分のkintoneドメインに変更(例:mycompany.cybozu.com)
    var APP_ID = '123';    // ← kintone管理画面で確認できるアプリID
    var API_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; // ← kintoneで発行したAPIトークン

    try {
      var response = https.get({
        url: 'https://' + KINTONE_DOMAIN + '/k/v1/records.json?app=' + APP_ID,
        headers: {
          'X-Cybozu-API-Token': API_TOKEN,  // ← ここが重要
          'Content-Type': 'application/json'
        }
      });

      log.debug('ステータス', response.code);
      log.debug('レスポンス', response.body);

      if (response.code === 200) {
        var records = JSON.parse(response.body).records;
        log.debug('取得件数', records.length);
        // ここでレコードを処理する
      }

    } catch (e) {
      log.error('エラー', e.message);
    }
  }

  return { execute: execute };
});

kintone 401エラーのチェックリスト

同じエラーで詰まっている方向けに、確認すべきポイントをまとめます。

ヘッダーの確認

  • [ ] X-Cybozu-API-Token ヘッダーを使っているか(Authorization: Bearer ではない)
  • [ ] トークンの文字列に余分なスペースや改行が入っていないか

kintone側の設定確認

  • [ ] APIトークンが有効になっているか(kintone管理画面で確認)
  • [ ] APIトークンに必要な権限(レコード閲覧・追加など)が付与されているか
  • [ ] IPアドレス制限が設定されている場合、NetSuiteのIPが許可されているか

NetSuite側の確認

  • [ ] N/https モジュールを使っているか(N/http ではなくHTTPSが必要)
  • [ ] スクリプトのデプロイ設定でネットワーク通信が許可されているか

まとめ

今回の401エラーの原因は一言でいうと、「kintone専用の認証ヘッダーを知らなかった」 ことでした。

一般的なREST APIは Authorization: Bearer {token} 形式が多いですが、kintoneは独自の X-Cybozu-API-Token ヘッダーを使います。このギャップに気づくまでに数時間かかりました。

同じところで詰まっている方の参考になれば幸いです。


関連記事


 #API連携 #401エラー #RESTlet #NetSuite #SuiteScript #kintonet