Skip to content

Users

Reached as client.users, which carries the user-record methods themselves plus three sub-namespaces for loans, fines and requests.

client.users

AlmaClientUserNS

AlmaClientUserNS(client: _AlmaExecutable)

Bases: BaseNamespace

Namespace for user functionality, exposed at client.users.

Carries the user-record methods themselves, plus three sub-namespaces:

Attribute Class
client.users.loans AlmaClientUserLoansNS
client.users.fines / .fees AlmaClientUserFinesNS
client.users.requests AlmaClientUserRequestsNS

get_users async Alma: Retrieve users

get_users(
    limit: int = ...,
    offset: int = ...,
    *,
    q: str | None = ...,
    order_by: Literal[
        "last_name", "first_name", "primary_id"
    ]
    | None = ...,
    expand: bool = ...,
    model: type[_ModelT],
) -> _ModelT
get_users(
    limit: int = ...,
    offset: int = ...,
    *,
    q: str | None = ...,
    order_by: Literal[
        "last_name", "first_name", "primary_id"
    ]
    | None = ...,
    expand: bool = ...,
    model: None = ...,
) -> RESP_TYPE

Search for users.

Parameters:

Name Type Description Default
limit int

Maximum number of users to return in this page. Note the default here is 10, not 100.

10
offset int

Index of the first user to return, for paging.

0
q str | None

Search query, in Alma's field~value form, e.g. "last_name~Smith" or "ALL~Smith" to search every indexed field. Every user is returned when omitted.

None
order_by Literal['last_name', 'first_name', 'primary_id'] | None

Field to sort the results by.

None
expand bool

When True, requests Alma's full view, which adds each user's fees and loans to the response. This costs Alma noticeably more work per user, so leave it off for plain searches.

False
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

A page of users, with total_record_count giving the full result size.

Raises:

Type Description
APIClientError

If the query is malformed or names an unindexed field.

Examples:

page = await client.users.get_users(
    q="last_name~Smith", limit=100, order_by="last_name"
)
for user in page.user:
    print(user.primary_id, user.last_name)

get_user async Alma: Get user details

get_user(user_id: str, *, model: type[_ModelT]) -> _ModelT
get_user(user_id: str, *, model: None = ...) -> RESP_TYPE

Retrieve a single user record.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The user record.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If another error occurred while making the API request.

Examples:

user = await client.users.get_user("12345678")
print(user.first_name, user.last_name, user.user_group.desc)

update_user async Alma: Update User Details

update_user(
    user_id: str, user: Body, *, model: type[_ModelT]
) -> _ModelT
update_user(
    user_id: str, user: Body, *, model: None = ...
) -> RESP_TYPE

Update a user record.

Alma replaces the whole user, so fetch it with get_user and modify that – a partial body drops the fields it omits, including roles and addresses.

Parameters:

Name Type Description Default
user_id str

The identifier of the user to update.

required
user Body

The full, modified user record. Accepts a mapping or any object implementing dump/model_dump, so a Pydantic model can be passed directly.

required
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The updated user record.

Raises:

Type Description
UserMissingFieldError

If a mandatory field is missing (Alma code 401664). The exception carries the user ID.

InvalidFieldError

If a field – often a role – is not valid.

UserNotFoundError

If no user matches the identifier.

APIClientError

If any other API client error occurs.

Examples:

user = await client.users.get_user("12345678")
user.contact_info.email[0].email_address = "new@example.ac.uk"
await client.users.update_user("12345678", user)

create_user async Alma: Create user

create_user(user: Body, *, model: type[_ModelT]) -> _ModelT
create_user(user: Body, *, model: None = ...) -> RESP_TYPE

Create a new user.

Being a POST, this call is not replayed if the response is lost in transit – a retry could create a duplicate user. See Rate limiting.

Parameters:

Name Type Description Default
user Body

The user record to create. Alma requires at least a primary_id, last_name, user_group, account_type and status. Accepts a mapping or any object implementing dump/model_dump.

required
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The newly created user record.

Raises:

Type Description
UserMissingFieldError

If a mandatory field is missing (Alma code 401664). The exception carries the primary_id from the body.

InvalidFieldError

If a field – often a role – is not valid.

APIClientError

If any other API client error occurs.

Examples:

user = await client.users.create_user(
    {
        "primary_id": "new.user@example.ac.uk",
        "first_name": "Ada",
        "last_name": "Lovelace",
        "user_group": {"value": "STAFF"},
        "account_type": {"value": "INTERNAL"},
        "status": {"value": "ACTIVE"},
    }
)

create_user_attachment async Alma: Create User Attachment

create_user_attachment(
    user_id: str,
    file_name: str,
    content: str,
    *,
    note: str = ...,
    description: str = ...,
    url: str = ...,
    model: type[_ModelT],
) -> _ModelT
create_user_attachment(
    user_id: str,
    file_name: str,
    content: str,
    *,
    note: str = ...,
    description: str = ...,
    url: str = ...,
    model: None = ...,
) -> RESP_TYPE

Attach a file to a user's record.

content is base64-encoded for you before it is sent – pass the plain text, not an already-encoded string. Note that it is typed as str and encoded as UTF-8, so this method handles text attachments only; binary files are not supported.

Being a POST, this call is not replayed if the response is lost in transit – a retry could attach the file twice. See Rate limiting.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
file_name str

Name to store the attachment under, including its extension.

required
content str

The file's text content, unencoded.

required
note str

Free-text note recorded against the attachment.

''
description str

Human-readable description shown in Alma.

''
url str

External URL for the attachment, as an alternative to inline content.

''
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The newly created attachment record.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If the attachment is rejected – for example when it exceeds Alma's size limit.

Examples:

attachment = await client.users.create_user_attachment(
    "12345678",
    "proof_of_address.txt",
    "Utility bill received 2026-07-01",
    description="Address verification",
)

client.users.loans

AlmaClientUserLoansNS

AlmaClientUserLoansNS(client: _AlmaExecutable)

Bases: BaseNamespace

Namespace for user loan functionality, exposed at client.users.loans.

Loans reached through a user. The same loans are also addressable through their bibliographic record via client.bibs.loans, which is the route to take when you know the item but not the borrower.

get_loans async Alma: Retrieve user loans

get_loans(
    user_id: str,
    limit: int = ...,
    offset: int = ...,
    order_by: Literal[
        "loan_date",
        "due_date",
        "barcode",
        "title",
        "author",
        "return_date",
    ] = ...,
    direction: Literal["asc", "desc"] = ...,
    expand: Literal["renewable"] | None = ...,
    loan_status: Literal["Active", "Complete"] = ...,
    *,
    model: type[_ModelT],
) -> _ModelT
get_loans(
    user_id: str,
    limit: int = ...,
    offset: int = ...,
    order_by: Literal[
        "loan_date",
        "due_date",
        "barcode",
        "title",
        "author",
        "return_date",
    ] = ...,
    direction: Literal["asc", "desc"] = ...,
    expand: Literal["renewable"] | None = ...,
    loan_status: Literal["Active", "Complete"] = ...,
    *,
    model: None = ...,
) -> RESP_TYPE

List a user's loans.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
limit int

Maximum number of loans to return in this page.

100
offset int

Index of the first loan to return, for paging.

0
order_by Literal['loan_date', 'due_date', 'barcode', 'title', 'author', 'return_date']

Field to sort by.

'due_date'
direction Literal['asc', 'desc']

Sort direction.

'asc'
expand Literal['renewable'] | None

Pass "renewable" to have Alma calculate and include each loan's renewability. This costs Alma extra work, so it is off by default.

None
loan_status Literal['Active', 'Complete']

"Active" for loans still out, "Complete" for returned ones.

'Active'
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

A page of loans, with total_record_count giving the full result size.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

Examples:

loans = await client.users.loans.get_loans(
    "12345678", expand="renewable", order_by="due_date"
)
for loan in loans.item_loan:
    print(loan.title, loan.due_date, loan.renewable)

create_loan async Alma: Create user loan

create_loan(
    user_id: str,
    item_barcode: str,
    circ_desk: str,
    library: str,
    request_id: str | None = ...,
    *,
    model: type[_ModelT],
) -> _ModelT
create_loan(
    user_id: str,
    item_barcode: str,
    circ_desk: str,
    library: str,
    request_id: str | None = ...,
    *,
    model: None = ...,
) -> RESP_TYPE

Loan an item to a user.

Being a POST, this call is not replayed if the response is lost in transit – a retry could create a duplicate loan. See Rate limiting.

Parameters:

Name Type Description Default
user_id str

The user identifier of the borrower.

required
item_barcode str

Barcode of the item to loan.

required
circ_desk str

Code of the circulation desk the loan is made at, as returned by client.config.libraries.get_circ_desks().

required
library str

Code of the library owning that circulation desk.

required
request_id str | None

Identifier of a request being fulfilled by this loan, when the loan satisfies an existing hold.

None
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The newly created loan record, including its loan_id and due_date.

Raises:

Type Description
BarcodeNotFoundError

If no item has that barcode.

IllegalBarcodeError

If the barcode is malformed.

UserNotFoundError

If no user matches the identifier.

LoanLimitError

If the user is already at their simultaneous-loan limit.

LoanBlockedError

If a block on the user prevents the loan.

ParallelLoanError

If the user already has another copy of this title out.

ItemAlreadyLoanedToUserError

If this item is already on loan to them.

ExpiredCardError

If the user's card has expired.

CannotBeLoanedError

If the item cannot be loaned from this desk.

UserIsNotAPatronError

If the user has no active patron role.

Examples:

loan = await client.users.loans.create_loan(
    "12345678", "39001234567890", circ_desk="DEFAULT", library="MAIN"
)
print(loan.loan_id, loan.due_date)

get_loan async Alma: Loan by user id and loan id

get_loan(
    user_id: str, loan_id: str, *, model: type[_ModelT]
) -> _ModelT
get_loan(
    user_id: str, loan_id: str, *, model: None = ...
) -> RESP_TYPE

Retrieve a single loan held by a user.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
loan_id str

The loan identifier.

required
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The loan record.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

LoanNotFoundError

If the user has no loan with that ID.

Examples:

loan = await client.users.loans.get_loan("12345678", "987654321")

renew_loan async Alma: Renew loan

renew_loan(
    user_id: str, loan_id: str, *, model: type[_ModelT]
) -> _ModelT
renew_loan(
    user_id: str, loan_id: str, *, model: None = ...
) -> RESP_TYPE

Renew a loan.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
loan_id str

The loan identifier.

required
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The renewed loan record, carrying its new due_date.

Raises:

Type Description
CannotRenewError

If Alma refused the renewal (code 401822) – because the item is requested by someone else, the renewal limit is reached, or a block applies. The exception carries the loan_id and Alma's own reason.

UserNotFoundError

If no user matches the identifier.

LoanNotFoundError

If the user has no loan with that ID.

Examples:

from almapy.exceptions import CannotRenewError

try:
    loan = await client.users.loans.renew_loan("12345678", "987654321")
    print("renewed to", loan.due_date)
except CannotRenewError as e:
    print("refused:", e.error)

change_loan_due_date async Alma: Change loan due date

change_loan_due_date(
    user_id: str,
    loan_id: str,
    due_date: str,
    *,
    notify_user: bool = ...,
    model: type[_ModelT],
) -> _ModelT
change_loan_due_date(
    user_id: str,
    loan_id: str,
    due_date: str,
    *,
    notify_user: bool = ...,
    model: None = ...,
) -> RESP_TYPE

Change a loan's due date.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
loan_id str

The loan identifier.

required
due_date str

The new due date, in ISO 8601 form with a Z suffix, e.g. "2026-09-30Z".

required
notify_user bool

Whether Alma should send the user a notification about the change.

False
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The updated loan record.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

LoanNotFoundError

If the user has no loan with that ID.

APIClientError

If the date is malformed or in the past.

Examples:

loan = await client.users.loans.change_loan_due_date(
    "12345678", "987654321", "2026-09-30Z", notify_user=True
)

client.users.fines

Also available as client.users.fees – Alma's own API calls these "fees", so both spellings refer to the same object.

AlmaClientUserFinesNS

AlmaClientUserFinesNS(client: _AlmaExecutable)

Bases: BaseNamespace

Namespace for user fines functionality, exposed at client.users.fines.

Also available as client.users.fees – Alma's own API calls these "fees" throughout, so both spellings are provided and refer to the same object.

Two ways to settle a balance: pay_fees pays against the user's whole account at once, while update_fee acts on one individual fee and can also waive, dispute or restore it.

get_fees class-attribute instance-attribute

get_fees = get_fines

Alias for get_fines.

get_fines async Alma: Get user fines/fees

get_fines(
    user_id: str,
    user_id_type: str = ...,
    status: Literal[
        "ACTIVE", "INDISPUTE", "EXPORTED", "CLOSED"
    ] = ...,
    *,
    model: type[_ModelT],
) -> _ModelT
get_fines(
    user_id: str,
    user_id_type: str = ...,
    status: Literal[
        "ACTIVE", "INDISPUTE", "EXPORTED", "CLOSED"
    ] = ...,
    *,
    model: None = ...,
) -> RESP_TYPE

List a user's fines and fees.

Also callable as client.users.fines.get_fees(...).

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
user_id_type str

Which kind of identifier user_id is. Defaults to "all_unique", matching against any of the user's unique IDs.

'all_unique'
status Literal['ACTIVE', 'INDISPUTE', 'EXPORTED', 'CLOSED']

"ACTIVE" for outstanding fees, "CLOSED" for settled ones, "INDISPUTE" for disputed, "EXPORTED" for those sent to an external bursar system.

'ACTIVE'
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The user's fees, with total_sum giving the balance across them.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

Examples:

fines = await client.users.fines.get_fines("12345678")
print("owes", fines.total_sum)
for fee in fines.fee:
    print(fee.id, fee.type.desc, fee.balance)

create_fee async Alma: Create user fine/fee

create_fee(
    user_id: str, fine: Body, *, model: type[_ModelT]
) -> _ModelT
create_fee(
    user_id: str, fine: Body, *, model: None = ...
) -> RESP_TYPE

Charge a fee to a user.

Being a POST, this call is not replayed if the response is lost in transit – a retry could charge the user twice. See Rate limiting.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
fine Body

The fee record to create. Requires at least a type from the FineFeeTypes code table and an original_amount. Accepts a mapping or any object implementing dump/model_dump.

required
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The newly created fee record, including its assigned id.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If the fee type is not valid or the amount is missing.

Examples:

fee = await client.users.fines.create_fee(
    "12345678",
    {
        "type": {"value": "LOSTITEMREPLACEMENTFEE"},
        "original_amount": 25.00,
        "comment": "Replacement charge",
    },
)

pay_fees async Alma: Pay user fines/fees

pay_fees(
    user_id: str,
    *,
    user_id_type: str = ...,
    amount: float,
    method: Literal["CREDIT_CARD", "ONLINE", "CASH"],
    comment: str | None = ...,
    external_transaction_id: str | None = ...,
    model: type[_ModelT],
) -> _ModelT
pay_fees(
    user_id: str,
    *,
    user_id_type: str = ...,
    amount: float,
    method: Literal["CREDIT_CARD", "ONLINE", "CASH"],
    comment: str | None = ...,
    external_transaction_id: str | None = ...,
    model: None = ...,
) -> RESP_TYPE

Pay against a user's whole fee balance.

Alma applies the payment across the user's active fees rather than to any one of them. To act on a single fee – or to waive rather than pay – use update_fee.

Being a POST, this call is not replayed if the response is lost in transit – a retry could take the payment twice. See Rate limiting.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
user_id_type str

Which kind of identifier user_id is.

'all_unique'
amount float

The amount to pay, in the institution's currency.

required
method Literal['CREDIT_CARD', 'ONLINE', 'CASH']

How the payment was taken.

required
comment str | None

Free-text note recorded against the transaction.

None
external_transaction_id str | None

Reference from the payment provider, for reconciling against an external system.

None
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The user's remaining fees after the payment is applied.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If the amount exceeds the outstanding balance or the method is not enabled for this institution.

Examples:

remaining = await client.users.fines.pay_fees(
    "12345678",
    amount=12.50,
    method="CREDIT_CARD",
    external_transaction_id="txn_9f2c",
)

get_fee async Alma: Get user fine/fee

get_fee(
    user_id: str,
    fee_id: str,
    *,
    user_id_type: str = ...,
    model: type[_ModelT],
) -> _ModelT
get_fee(
    user_id: str,
    fee_id: str,
    *,
    user_id_type: str = ...,
    model: None = ...,
) -> RESP_TYPE

Retrieve a single fee.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
fee_id str

The fee identifier.

required
user_id_type str

Which kind of identifier user_id is.

'all_unique'
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The fee record, including its original_amount, balance and any

Any

transactions against it.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If the user has no fee with that ID.

Examples:

fee = await client.users.fines.get_fee("12345678", "987654321")
print(fee.balance, fee.status.value)

update_fee async Alma: Pay/waive/dispute/restore user fine/fee

update_fee(
    user_id: str,
    fee_id: str,
    *,
    op: Literal["pay", "waive", "dispute", "restore"],
    user_id_type: str = ...,
    amount: float,
    method: Literal["CREDIT_CARD", "ONLINE", "CASH"]
    | None = ...,
    reason: str | None = ...,
    comment: str | None = ...,
    external_transaction_id: str | None = ...,
    model: type[_ModelT],
) -> _ModelT
update_fee(
    user_id: str,
    fee_id: str,
    *,
    op: Literal["pay", "waive", "dispute", "restore"],
    user_id_type: str = ...,
    amount: float,
    method: Literal["CREDIT_CARD", "ONLINE", "CASH"]
    | None = ...,
    reason: str | None = ...,
    comment: str | None = ...,
    external_transaction_id: str | None = ...,
    model: None = ...,
) -> RESP_TYPE

Pay, waive, dispute or restore a single fee.

The counterpart to pay_fees, which works across the user's whole balance instead.

Being a POST, this call is not replayed if the response is lost in transit. See Rate limiting.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
fee_id str

The fee identifier.

required
op Literal['pay', 'waive', 'dispute', 'restore']

What to do – "pay" settles it, "waive" cancels the charge, "dispute" marks it as contested, "restore" reverses a previous waiver or dispute.

required
user_id_type str

Which kind of identifier user_id is.

'all_unique'
amount float

The amount to act on. May be less than the balance for a partial payment or waiver.

required
method Literal['CREDIT_CARD', 'ONLINE', 'CASH'] | None

How the payment was taken. Required for op="pay", ignored otherwise.

None
reason str | None

Justification code, required for op="waive". Values come from the PaymentAndWaiveReasons code table.

None
comment str | None

Free-text note recorded against the transaction.

None
external_transaction_id str | None

Reference from the payment provider.

None
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The updated fee record.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If the fee does not exist, the amount exceeds its balance, or a required argument for the chosen op is missing.

Examples:

Waive a fee in full:

fee = await client.users.fines.get_fee("12345678", "987654321")
await client.users.fines.update_fee(
    "12345678",
    "987654321",
    op="waive",
    amount=float(fee.balance),
    reason="LIBRARYERROR",
    comment="Item was returned on time",
)

client.users.requests

AlmaClientUserRequestsNS

AlmaClientUserRequestsNS(client: _AlmaExecutable)

Bases: BaseNamespace

Namespace for user requests, exposed at client.users.requests.

Requests are holds, digitisation requests and bookings placed on behalf of a user. The same requests can also be reached from the bibliographic side via client.bibs.requests, which is where to look when starting from a record rather than a borrower.

get_request async Alma: Retrieve user request

get_request(
    user_id: str, request_id: str, *, model: type[_ModelT]
) -> _ModelT
get_request(
    user_id: str, request_id: str, *, model: None = ...
) -> RESP_TYPE

Retrieve a single request placed by a user.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
request_id str

The request identifier.

required
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The request record.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If the user has no request with that ID.

Examples:

req = await client.users.requests.get_request("12345678", "987654321")
print(req.request_status, req.pickup_location)

get_requests async Alma: Retrieve user requests

get_requests(
    user_id: str,
    *,
    request_type: Literal["HOLD", "DIGITIZATION", "BOOKING"]
    | None = ...,
    user_id_type: str = ...,
    limit: int = ...,
    offset: int = ...,
    status: Literal["active", "history"] = ...,
    model: type[_ModelT],
) -> _ModelT
get_requests(
    user_id: str,
    *,
    request_type: Literal["HOLD", "DIGITIZATION", "BOOKING"]
    | None = ...,
    user_id_type: str = ...,
    limit: int = ...,
    offset: int = ...,
    status: Literal["active", "history"] = ...,
    model: None = ...,
) -> RESP_TYPE

List the requests placed by a user.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
request_type Literal['HOLD', 'DIGITIZATION', 'BOOKING'] | None

Restrict to one kind of request. All kinds are returned when omitted.

None
user_id_type str

Which kind of identifier user_id is.

'all_unique'
limit int

Maximum number of requests to return in this page. Note the default here is 10, not 100.

10
offset int

Index of the first request to return, for paging.

0
status Literal['active', 'history']

"active" for outstanding requests, "history" for completed and cancelled ones.

'active'
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

A page of requests, with total_record_count giving the full result

Any

size.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

Examples:

reqs = await client.users.requests.get_requests(
    "12345678", request_type="HOLD", limit=100
)
for req in reqs.user_request:
    print(req.request_id, req.title, req.request_status)

create_request async Alma: Create user request

create_request(
    user_id: str,
    request: Request,
    user_id_type: str = ...,
    *,
    allow_same_request: bool = ...,
    mms_id: str = ...,
    item_id: str = ...,
    model: type[_ModelT],
) -> _ModelT
create_request(
    user_id: str,
    request: Request,
    user_id_type: str = ...,
    *,
    allow_same_request: bool = ...,
    mms_id: str = ...,
    item_id: str = ...,
    model: None = ...,
) -> RESP_TYPE

Place a request on behalf of a user.

A request is placed against either a bibliographic record or a specific item, never both: pass exactly one of mms_id and item_id. A title level request (mms_id) lets Alma pick any suitable copy; an item level request (item_id) pins it to one.

Being a POST, this call is not replayed if the response is lost in transit – a retry could place a duplicate request. See Rate limiting.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
request Request

The request to create. Requires at least request_type and, for holds, a pickup_location_type and pickup_location_library.

required
user_id_type str

Which kind of identifier user_id is.

'all_unique'
allow_same_request bool

Whether to permit a second request when the user already has one on this title.

False
mms_id str

MMS ID for a title level request.

''
item_id str

Item PID for an item level request.

''
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The newly created request record, including its request_id.

Raises:

Type Description
ValueError

If both or neither of mms_id and item_id are given. Raised locally, before any request is made.

RequestFailedError

If Alma rejected the request (code 401873).

NoItemsCanFulfillRequestError

If no item can satisfy it (code 401129).

ParallelRequestError

If the user already has a request on another copy and allow_same_request is False.

UserNotFoundError

If no user matches the identifier.

MMSIdNotFoundError

If the MMS ID does not exist.

Examples:

req = await client.users.requests.create_request(
    "12345678",
    {
        "request_type": "HOLD",
        "pickup_location_type": "LIBRARY",
        "pickup_location_library": "MAIN",
    },
    mms_id="99123456789012345",
)

update_request async Alma: Update request

update_request(
    user_id: str,
    request_id: str,
    request: Request,
    *,
    model: type[_ModelT],
) -> _ModelT
update_request(
    user_id: str,
    request_id: str,
    request: Request,
    *,
    model: None = ...,
) -> RESP_TYPE

Update a request.

Alma replaces the whole request, so fetch it with get_request and modify that rather than sending a partial body. Not every field is editable – the pickup location and expiry date generally are; the request type is not.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
request_id str

The request identifier.

required
request Request

The full, modified request record.

required
model Any

Optional Pydantic model class to validate the response into.

None

Returns:

Type Description
Any

The updated request record.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If the request does not exist, or a field that cannot be changed was modified.

Examples:

req = await client.users.requests.get_request("12345678", "987654321")
req.pickup_location_library = "SCIENCE"
await client.users.requests.update_request("12345678", "987654321", req)

cancel_request async Alma: Cancel user request

cancel_request(
    user_id: str,
    request_id: str,
    reason: str,
    *,
    notify_user: bool,
    note: str | None = None,
) -> None

Cancel a request placed by a user.

Parameters:

Name Type Description Default
user_id str

The user identifier.

required
request_id str

The request identifier.

required
reason str

Cancellation reason. Must be a code from the RequestCancellationReasons code table – fetch the valid values with client.config.code_tables.get_code_table().

required
notify_user bool

Whether Alma should notify the user of the cancellation.

required
note str | None

Free-text note included in the notification.

None

Returns:

Type Description
None

None. Alma returns an empty body on success.

Raises:

Type Description
UserNotFoundError

If no user matches the identifier.

APIClientError

If the request does not exist, is already fulfilled, or the reason code is not valid.

Examples:

await client.users.requests.cancel_request(
    "12345678",
    "987654321",
    reason="CannotBeFulfilled",
    notify_user=True,
    note="Item is now missing",
)