# Procedures

To implement a procedure in a custom book, define a Python function in your book class and decorate it with [**@procedure**](/legacy/legacy-experience/books/custom-books/api-reference/decorators/procedure-decorator.md)**.**

## Requirements

### 1. Naming

Ensure the name of your procedure adheres to the syntax guidelines specified in the `name` parameter of the [@procedure](/legacy/legacy-experience/books/custom-books/api-reference/decorators/procedure-decorator.md) decorator.

### 2. Method Docstrings

Your method [docstring](/legacy/legacy-experience/books/custom-books/api-reference/docstrings.md) should include the following sections:

* A brief summary of the procedure
* Input Concepts
* Output Concepts

Examples are not required but are valuable for generating usage documentation.

### 3. Concept-Parameter Matching

Concepts and parameters must **match** to ensure they are properly mapped internally. Ensure your method definition adheres to the [guidelines](#id-3.-concept-parameter-matching).

## Using Procedures in Your Automation

### Singularized Calls

A **singularized call** is a way to call a procedure by phrasing it as if it returns a single item, even though it returns a list by definition. A procedure supports singularized calls in addition to standard calls if it meets ***all*** of the following conditions:

* **Returns a list**.
* The **output** of the procedure is the object itself.
* The **output noun phrase** is plural.
* The procedure **accepts filters.**

{% hint style="success" %}
You don't need to implement additional logic for singular calls. The system will automatically generate the singularized variation of any procedure that meets the above criteria.
{% endhint %}

#### Example

Consider a procedure that retrieves users from Outlook. It can be called in two ways:

* **Standard Way**: `get some users from outlook whose whose mail is "example.com"`
* **Singularized Way**: `get a user from outlook whose whose mail is "example.com"`

```python
@procedure("to get some (users) from *office365*")
async def retrieve_users(self, filter_expression: Optional[FilterExpression] = None) -> List[OfficeUser]:
  """
  Retrieves Office 365 users accessible via the Microsoft Graph API.

  It requires the following permissions on the application:
  User.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All

  Returns:
  A list of Office 365 users.

  Example 1:
  Retrieve all users

  >>> get users from office365

  Example 2:
  Retrieve a user whose email matches the specified email address

  >>> get users from office365 whose mail is "john@acme.org"
  """
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.kognitos.com/legacy/legacy-experience/books/custom-books/api-reference/procedures.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
