コンテンツにスキップ

ユーザ更新

概要

APIを使用してユーザを更新することができます。

事前準備

APIの操作を行う前にAPIキーの作成を実施してください。
また、この機能はテナント管理者でないと行えないため、ユーザ管理からテナント管理者の設定を行ってください。

リクエスト

下記のリクエスト形式で、jsonデータを送信します。

設定項目 値
HTTPメソッド POST
Content-Type application/json
文字コード UTF-8
URL http://{サーバ名}/api/users/{ユーザID}/update(※1)
Body 以下のjsonデータを参考のこと

(※1){サーバ名}、{ユーザID}の部分は、適宜、環境に合わせて編集してください。
  pleasanter.netの場合は以下の形式になります。
  https://pleasanter.net/fs/api/users/{ユーザID}/update

設定値

設定値はスクリプトの$p.apiUsersUpdateでも指定できます。

パラメータ名 型 必須 概要
ApiVersion decimal APIのバージョン。
ApiKey string ○1 事前に作成したAPIキー。
JSONデータレイアウト object ○ 更新するユーザの情報。詳細は「JSONデータレイアウト:User」を参照
JSON
{
    "ApiVersion": 1.1,
    "ApiKey": "8s63Aiigd98h51Dfda7951...",
    "Name": "[更新後のユーザーの名前]",
    "MailAddresses": [
        "webmaster@example.com",
        "info@example.com"
    ],
    "ClassHash": {
        "ClassA": "test-class-column"
    },
    "NumHash": {
        "NumA": 100
    },
    "DateHash": {
        "DateA": "2026-08-19"
    },
    "DescriptionHash": {
        "DescriptionA": "補足説明"
    },
    "CheckHash": {
        "CheckA": true
    }
}

※メールアドレスを更新する場合はMailAddressesにリスト形式で記述します(任意)。1つのユーザに対して単一または複数のメールアドレスを設定することが可能です。洗い替え方式で更新されます(古いメールアドレス情報を削除して新しいメールアドレス情報を追加する)。

レスポンス

下記の形式のjsonデータが返却されます。

JSON
{
    "Id": 12345,
    "StatusCode": 200,
    "Message": "\" ユーザーの名前を更新 \" を更新しました。"
}

メールアドレスの設定について

以下のようにMailAddressesのパラメータを指定した上でリストを空にした場合はメールアドレスが削除されます。

JSON
{
    "ApiVersion": 1.1,
    "ApiKey": "8s63Aiigd98h51Dfda7951...",
    "Name": "[更新後のユーザーの名前]",
    "MailAddresses": [
    ]
}

以下のようにMailAddressesのパラメータを指定しない場合はメールアドレスは更新(削除)されません。

JSON
{
    "ApiVersion": 1.1,
    "ApiKey": "8s63Aiigd98h51Dfda7951...",
    "Name": "[更新後のユーザーの名前]"
}

サンプルコード

Note

コード内の【 ... 】は適宜修正してください。

1. 外部要員の契約終了日をログイン有効期限にまとめて反映する

派遣・業務委託・常駐など、契約期間が決まっている外部要員のアカウントについて、契約終了日をユーザーの「ログイン有効期限」にまとめて反映します。「ログインID・契約終了日」のCSVを読み込み、ユーザーごとに現在のログイン有効期限と比べて、違うものに限ってユーザー更新APIで書き換えます。ログイン有効期限には、契約終了日(ログインできる最終日)の翌日0時を設定します。 契約終了日が空の行は無期限とみなし、ログイン有効期限を未設定に戻します。

実務での使いどころ

ログイン有効期限はユーザー管理の編集画面で1人ずつ設定する項目で、ユーザーのCSVインポートでは取り込めません。外部要員の入れ替わりが多い現場では、契約が終わったアカウントの無効化を忘れると、退場した人がログインできる状態が残ります。

契約終了日をあらかじめログイン有効期限に入れておけば、無効化の作業を忘れても期限が来た時点でログインできなくなります。契約が延長されたときは、同じCSVの契約終了日を書き換えて再実行すれば済みます。現在の値と同じユーザーは更新しないため、毎回全員分のCSVを流しても問題ありません。

事前準備

1:テナント管理者権限のAPIキーを用意してください(ユーザーの取得・更新APIの前提です)。
2:ユーザーはあらかじめ作成しておいてください(このサンプルはユーザーを作成しません)。
3:以下の形式のCSVファイル(UTF-8)を用意してください。列名と日付の形式は、サンプルコードのLOGIN_ID_COLUMN・END_DATE_COLUMN・END_DATE_FORMATで変更できます。

列名 内容
LoginId ログインID。プリザンターのユーザーと突合するキー
ContractEndDate 契約終了日(ログインできる最終日)。空欄なら無期限
LoginId,ContractEndDate
haken-001,2026/12/31
itaku-002,2027/03/31
itaku-003,

4:下記サンプルコードのAPI_KEY・INPUT_CSV・OUTPUT_CSVを環境にあわせて書き換えてください。
5:最初はDRY_RUN = Trueのまま実行し、結果CSV(result.csv)で予定の変更を確認してから、DRY_RUN = Falseにして実行してください。

結果CSVには、ログインID・名前・契約終了日・変更前・変更後・結果(「更新」「変更なし」「ユーザーなし」「更新失敗」)が出力されます。

Python
# ==========================================================
# 派遣・業務委託など契約期間の決まっている外部要員について、契約終了日を
# ユーザーの「ログイン有効期限」にまとめて反映するサンプルです。
# 「ログインID・契約終了日」のCSVを読み込み、ユーザーごとに現在の有効期限と比べて、
# 違うものだけをユーザー更新API(users/{ユーザーID}/update)で書き換えます。
# 契約が延長されたときは、同じCSVを更新して再実行してください。
#
# 【契約終了日の翌日0時を設定する理由】
#   ログイン有効期限は時分秒まで持つ日時で、その日時になった時点からログインできなく
#   なります。契約終了日(ログインできる最終日)をそのまま設定すると、最終日の0時で
#   ログインできなくなるため、最終日の翌日0時を設定します。
#   (例:2026/10/31まで使えるなら2026/11/01 0時を設定)
#
# 【契約終了日が空の行】
#   無期限とみなし、ログイン有効期限を未設定に戻します。
#   空文字やnullを送っても値は消えないため、未設定を表す"1899/12/30"を送ります。
# ==========================================================
import csv
import datetime
from pathlib import Path

import requests

# ==============================
# Pleasanter 接続設定
# ==============================
# 【重要】API_KEYは必ずテナント管理者権限のユーザーで発行したものを使用してください。
BASE_URL = "【URL】"
API_KEY = "【APIキー】"

API_VERSION = 1.1

PAGE_SIZE = 200  # ユーザー取得時の1ページあたりの件数

# ==============================
# ファイル設定(環境にあわせ適宜変更してください)
# ==============================
INPUT_CSV = Path("./input/contract_end.csv")
OUTPUT_CSV = Path("./output/result.csv")

# 入力CSVの列名と日付の形式(人事・契約管理システムの出力にあわせて変更してください)
LOGIN_ID_COLUMN = "LoginId"
END_DATE_COLUMN = "ContractEndDate"
END_DATE_FORMAT = "%Y/%m/%d"

# True の場合、実際の更新は行わず予定の変更だけを結果CSVに出力する
DRY_RUN = True

# ログイン有効期限の「未設定」を表す値(実機確認済み。バージョンアップで変わる可能性あり)
# users/getでは"1899-12-30T00:00:00"で返り、未設定に戻すときはこの日付を送る
UNSET_LIMIT = datetime.datetime(1899, 12, 30)


# ==============================
# CSV入出力
# ==============================
def read_csv(path):
    with open(path, "r", encoding="utf-8-sig", newline="") as f:
        return [
            {k.strip(): (v or "").strip() for k, v in row.items()}
            for row in csv.DictReader(f)
        ]


def write_result_csv(path, results):
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(path, "w", encoding="utf-8-sig", newline="") as f:
        writer = csv.DictWriter(
            f,
            fieldnames=["LoginId", "Name", "ContractEndDate", "Before", "After", "Result"],
        )
        writer.writeheader()
        writer.writerows(results)


# ==============================
# Pleasanter API操作
# ==============================
def post_json(url, payload):
    r = requests.post(
        url,
        json=payload,
        headers={"Content-Type": "application/json"},
        timeout=60,
    )
    r.raise_for_status()
    return r.json()


# ==============================
# ユーザー取得(ページングで全件)
# ==============================
def get_users_by_login_id():
    users = {}
    offset = 0

    while True:
        data = post_json(
            f"{BASE_URL}/api/users/get",
            {
                "ApiVersion": API_VERSION,
                "ApiKey": API_KEY,
                "Offset": offset,
                "PageSize": PAGE_SIZE,
            },
        )
        page = data["Response"]["Data"]
        for user in page:
            users[user["LoginId"]] = user

        offset += len(page)  # 固定値ではなく、取得件数に応じて進める
        if len(page) == 0 or offset >= data["Response"]["TotalCount"]:
            return users


# ==============================
# ログイン有効期限の更新
# ==============================
def update_login_expiration(user_id, limit):
    # LoginExpirationLimitだけを送ると、その項目だけが更新される(名前などは変わらない)
    value = (limit or UNSET_LIMIT).strftime("%Y/%m/%d")
    data = post_json(
        f"{BASE_URL}/api/users/{user_id}/update",
        {
            "ApiVersion": API_VERSION,
            "ApiKey": API_KEY,
            "LoginExpirationLimit": value,
        },
    )
    if data.get("StatusCode") != 200:
        raise RuntimeError(data.get("Message"))


# ==============================
# 日付の変換
# ==============================
def to_login_expiration_limit(end_date_text):
    """契約終了日(最終日)から、設定するログイン有効期限(翌日0時)を求める。空ならNone(無期限)"""
    if not end_date_text:
        return None
    last_day = datetime.datetime.strptime(end_date_text, END_DATE_FORMAT)
    return last_day + datetime.timedelta(days=1)


def parse_current_limit(limit_text):
    """users/getのLoginExpirationLimitを変換する。未設定ならNone"""
    if not limit_text:
        return None
    limit = datetime.datetime.fromisoformat(limit_text)
    if limit == UNSET_LIMIT:
        return None
    return limit


def format_limit(limit):
    return limit.strftime("%Y/%m/%d %H:%M") if limit else "未設定"


# ==============================
# メイン処理
# ==============================
def main():
    rows = read_csv(INPUT_CSV)
    # 日付の形式誤りは、1件も更新しないうちに検出する
    plans = [
        (row[LOGIN_ID_COLUMN], row[END_DATE_COLUMN], to_login_expiration_limit(row[END_DATE_COLUMN]))
        for row in rows
    ]

    users = get_users_by_login_id()

    results = []
    for login_id, end_date_text, new_limit in plans:
        user = users.get(login_id)
        if user is None:
            results.append({
                "LoginId": login_id,
                "Name": "",
                "ContractEndDate": end_date_text,
                "Before": "",
                "After": "",
                "Result": "ユーザーなし",
            })
            continue

        current_limit = parse_current_limit(user.get("LoginExpirationLimit"))
        result = {
            "LoginId": login_id,
            "Name": user.get("Name", ""),
            "ContractEndDate": end_date_text,
            "Before": format_limit(current_limit),
            "After": format_limit(new_limit),
        }

        if current_limit == new_limit:
            result["Result"] = "変更なし"
        elif DRY_RUN:
            result["Result"] = "更新"
            print(f"[UPDATE(dry-run)] {login_id} {result['Before']} -> {result['After']}")
        else:
            try:
                update_login_expiration(user["UserId"], new_limit)
                result["Result"] = "更新"
                print(f"[UPDATE] {login_id} {result['Before']} -> {result['After']}")
            except (requests.RequestException, RuntimeError) as e:
                result["Result"] = "更新失敗"
                print(f"[ERROR] {login_id} {e}")

        results.append(result)

    write_result_csv(OUTPUT_CSV, results)

    print("=== 結果 ===" + ("(DRY_RUN:実際には更新していません)" if DRY_RUN else ""))
    for kind in ["更新", "変更なし", "ユーザーなし", "更新失敗"]:
        count = sum(1 for r in results if r["Result"] == kind)
        print(f"{kind}: {count}件")
    print(f"結果CSV: {OUTPUT_CSV}")


if __name__ == "__main__":
    main()

注記

  • ログイン有効期限は時分秒まで持つ日時で、その日時になった時点からログインできなくなります。契約終了日をそのまま設定すると最終日にログインできないため、サンプルでは翌日0時を設定しています。
  • ログイン有効期限を未設定に戻すとき、空文字やnullを送っても値は消えません。サンプルでは未設定を表す"1899/12/30"を送っています。users/getは、未設定のときに"1899-12-30T00:00:00"を返します。これらはマニュアルに記載の無い挙動です。導入前に、使用するバージョンで同様に動作するか確認してください。
  • users/{ユーザーID}/updateにLoginExpirationLimitのみを送ると、その項目のみが更新され、名前などほかの項目は変わりません。
  • 期限が近いユーザーを確認したいときは、users/getのViewで"ColumnFilterHash": {"LoginExpirationLimit": "[\"2026/12/01,2026/12/31\"]"}のように範囲を指定して絞り込めます。

対応バージョン

対応バージョン 内容
1.3.22.0 以降 メールアドレスをAPIで更新する機能を追加
1.5.8.0 以降 拡張項目をAPIで更新する機能を追加

エラー時の確認事項

・API使用時の注意点やエラーが発生する場合の確認事項
・FAQ:変更後の設定ファイルやAPIリクエスト(JSON形式)が正しく認識されない場合の確認事項

仕様変更について

※ 2018年11月よりAPIの仕様が一部変更となりました。
- URLの形式が '/pleasanter/api_items/xxxx' から '/pleasanter/api/items/xxxx' に変更されました。
- Content-Type の指定が'application/x-www-form-urlencoded' から 'application/json'に変更されました。


  1. APIキーはAPIでは指定必須。スクリプトおよびサーバスクリプトは任意指定で、指定した場合はAPIキーのユーザの権限で処理を実行。 ↩