株式会社etika(エティカ)CRMサポートセンターです。

「Zoho CRMのデータを自前のスクリプトから読み書きしたい」「バッチ処理でまとめて項目を更新したい」——このとき最初の関門になるのがAPI認証です。この記事では、リフレッシュトークンの管理が不要で、サーバー間連携に向いた**Client Credentials方式(Self Client)**でアクセストークンを取得する手順を、実際に動くNode.jsコードで解説します。

結論(先に要点)

  • Zoho API Console で Self Client を作り、Client ID / Client Secret を取得する
  • grant_type=client_credentialssoid=ZohoCRM.<組織ID> を付けてトークンを取得する
  • データセンター(dc)ごとに accountsapi_domain のURLが異なる点に注意する

認証方式の選び方

Zoho APIの認証にはいくつか方式がありますが、用途で選びます。

方式 向き リフレッシュトークン
Authorization Code ユーザー操作を伴うアプリ 必要
Client Credentials(Self Client) サーバー間・バッチ処理 不要

社内バッチや管理用スクリプトのように「特定の組織のデータを、ユーザーのログインなしに操作する」ケースでは、Client Credentials方式がシンプルで運用も楽です。

Zoho APIの認証方式の使い分け

手順1:Self Clientを作成する

  1. Zoho API Console(日本DCなら api-console.zoho.jp)へアクセス
  2. 「Self Client」を選択して作成
  3. 発行された Client IDClient Secret を控える

あわせて、対象組織の**組織ID(zgid)**を用意します(CRMの「設定 > 会社情報」等で確認できます)。

手順2:必要なscopeを決める

やりたい操作に対応するscopeだけを付けます。例:

  • レコードの読み書き:ZohoCRM.modules.ALL
  • 項目・レイアウトなどの設定:ZohoCRM.settings.ALL
  • 項目定義の読み書きに絞る:ZohoCRM.settings.fields.ALL

必要最小限にするのがセキュリティ上の基本です。

無料相談

Zoho APIを使った連携やバッチ処理、自社で実現したい構想はありませんか?無料でご相談を承ります。

Zoho API連携を無料で相談する

手順3:トークンを取得する(Node.js)

Client Credentials方式のポイントは、soidパラメータにZohoCRM.<組織ID>を渡すことです。これで「どの組織を操作するか」を指定します。

function accountsBaseUrl(dc) {
  switch ((dc || "").toLowerCase()) {
    case "jp": return "https://accounts.zoho.jp";
    case "eu": return "https://accounts.zoho.eu";
    case "in": return "https://accounts.zoho.in";
    case "au": return "https://accounts.zoho.com.au";
    default:   return "https://accounts.zoho.com";
  }
}

async function getToken({ dc, clientId, clientSecret, orgId, scope }) {
  const body = new URLSearchParams();
  body.set("client_id", clientId);
  body.set("client_secret", clientSecret);
  body.set("grant_type", "client_credentials");
  body.set("scope", scope);
  body.set("soid", `ZohoCRM.${orgId}`);   // ← 操作対象の組織を指定

  const res = await fetch(`${accountsBaseUrl(dc)}/oauth/v2/token`, {
    method: "POST",
    headers: { "content-type": "application/x-www-form-urlencoded" },
    body,
  });
  const json = await res.json();
  if (!res.ok || json.error || !json.access_token) {
    throw new Error(`トークン取得に失敗: ${JSON.stringify(json)}`);
  }
  return {
    accessToken: json.access_token,
    apiDomain: (json.api_domain || "").replace(/\/+$/, ""),
  };
}

手順4:取得したトークンでAPIを叩く

レスポンスに含まれるapi_domain必ず使うのがコツです。DCによってAPIのドメインが異なるため、ハードコードせずレスポンス値を使います。

async function crmFetch(token, path, { method = "GET", body } = {}) {
  const res = await fetch(`${token.apiDomain}${path}`, {
    method,
    headers: {
      Authorization: `Zoho-oauthtoken ${token.accessToken}`,
      ...(body ? { "content-type": "application/json" } : {}),
    },
    ...(body ? { body: JSON.stringify(body) } : {}),
  });
  if (res.status === 204) return null;
  const json = await res.json();
  if (!res.ok) throw new Error(`API ${method} ${path} failed: ${JSON.stringify(json)}`);
  return json;
}

// 使用例:商談モジュールの項目一覧を取得
const token = await getToken({
  dc: "jp",
  clientId: process.env.ZOHO_CLIENT_ID,
  clientSecret: process.env.ZOHO_CLIENT_SECRET,
  orgId: process.env.ZOHO_ORG_ID,
  scope: "ZohoCRM.settings.fields.ALL",
});
const fields = await crmFetch(token, "/crm/v8/settings/fields?module=Deals");
console.log(fields.fields.map((f) => `${f.field_label} = ${f.api_name}`));

トークン取得からAPI呼び出しまでの流れ

つまずきやすいポイント

  • missing_org_info エラーsoid=ZohoCRM.<組織ID>の付け忘れ、または組織IDの誤りが原因です。
  • DCの取り違え:日本DCの組織ならaccounts.zoho.jp.comに投げると認証が通りません。api_domainも同様にレスポンス値を使います。
  • scope不足INVALID_SCOPEや権限エラーが出たら、操作に対応するscopeが付いているか確認します。
  • Client SecretはGitに載せない.envに置き、環境変数から読み込みます。

まとめ

Client Credentials方式(Self Client)なら、リフレッシュトークンの更新運用に悩まされることなく、スクリプトからZoho APIを叩けます。ポイントは**soidで組織を指定することDCごとのURL・api_domainを正しく使うこと**の2点。ここさえ押さえれば、項目一覧の取得からレコードの一括更新まで自動化の土台が整います。

無料相談

認証設定の先にある「やりたい自動化」を形にしませんか?要件整理からAPI連携まで無料でご相談いただけます。

Zoho API連携を無料で相談する