# `Ash.ActionInput`
[🔗](https://github.com/ash-project/ash/blob/v3.17.0/lib/ash/action_input.ex#L5)

Input for a custom action

Much like an `Ash.Query` and `Ash.Changeset` are used to provide inputs into
CRUD actions, this struct provides the inputs required to execute a generic
action.

# `after_action_fun`

```elixir
@type after_action_fun() :: (t(), term() -&gt;
                         :ok
                         | {:ok, [Ash.Notifier.Notification.t()]}
                         | {:ok, term()}
                         | {:ok, term(), [Ash.Notifier.Notification.t()]}
                         | {:error, any()})
```

Function type for after action hooks.

Receives the action input and the result of the action, and can return
the result optionally with notifications, or an error.

# `after_transaction_fun`

```elixir
@type after_transaction_fun() :: (t(), :ok | {:ok, term()} | {:error, any()} -&gt;
                              :ok | {:ok, term()} | {:error, any()})
```

Function type for after transaction hooks.

Receives the action input and the result of the transaction, and returns
the result (potentially modified) or an error.

# `around_transaction_fun`

```elixir
@type around_transaction_fun() :: (t(),
                             (t() -&gt; :ok | {:ok, term()} | {:error, any()}) -&gt;
                               :ok | {:ok, term()} | {:error, any()})
```

Function type for around transaction hooks.

Receives an action input and a callback function that executes the transaction,
and returns the result of calling the callback or an error.

# `before_action_fun`

```elixir
@type before_action_fun() :: (t() -&gt;
                          t()
                          | {t(),
                             %{notifications: [Ash.Notifier.Notification.t()]}})
```

Function type for before action hooks.

Receives an action input and returns a modified action input, optionally with notifications.

# `before_transaction_fun`

```elixir
@type before_transaction_fun() :: (t() -&gt; t() | {:error, any()})
```

Function type for before transaction hooks.

Receives an action input and returns a modified action input or an error.

# `t`

```elixir
@type t() :: %Ash.ActionInput{
  action: Ash.Resource.Actions.Action.t() | nil,
  after_action: [after_action_fun()],
  after_transaction: [after_transaction_fun()],
  arguments: map(),
  around_transaction: [around_transaction_fun()],
  before_action: [before_action_fun()],
  before_transaction: [before_transaction_fun()],
  context: map(),
  domain: Ash.Domain.t() | nil,
  errors: [Ash.Error.t()],
  invalid_keys: MapSet.t(),
  load: keyword(keyword()),
  params: map(),
  resource: Ash.Resource.t(),
  tenant: term(),
  to_tenant: term(),
  valid?: boolean()
}
```

An action input struct for generic (non-CRUD) actions.

Contains all the information needed to execute a generic action including
arguments, context, tenant information, validation state, and lifecycle hooks. Built using
`for_action/4` and modified with functions like `set_argument/3` and `set_context/2`.

# `add_error`

```elixir
@spec add_error(
  t(),
  Ash.Error.error_input() | [Ash.Error.error_input()],
  Ash.Error.path_input()
) :: t()
```

Adds an error to the errors list and marks the action input as invalid.

This function allows you to add validation errors or other issues to the
action input. Once an error is added, the input will be marked as invalid
and action execution will be prevented.

## Examples

    # Add a simple string error
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_notification, %{})
    ...> |> Ash.ActionInput.add_error("Missing required configuration")
    iex> input.valid?
    false

    # Add an error with a specific path
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.for_action(:process_data, %{})
    ...> |> Ash.ActionInput.add_error("Invalid format", [:data, :format])
    iex> input.errors |> List.first() |> Map.get(:path)
    [:data, :format]

    # Add multiple errors
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.for_action(:complex_action, %{})
    ...> |> Ash.ActionInput.add_error(["Error 1", "Error 2"])
    iex> length(input.errors)
    2

    # Add structured error with keyword list
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:validate_input, %{})
    ...> |> Ash.ActionInput.add_error(field: :email, message: "is invalid")

## See also

- `Ash.Error.to_ash_error/3` for more on supported error values
- Action implementations can use this to add custom validation errors
- `set_argument/3` automatically adds errors for invalid argument values

# `after_action`

```elixir
@spec after_action(
  input :: t(),
  fun :: after_action_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds an after_action hook to the action input.

After action hooks are called with the action input and the result returned
from the action. They can modify the result, perform side effects, or return
errors to halt processing. The hook can return notifications alongside the result.

> #### Actions without a return type {: .tip}
>
> After action hooks will work for generic actions without a return type,
> however they will receive `nil` as their `result` argument and are
> expected to return
> `:ok | {:ok, [Ash.Notifier.Notification.t()]} | {:error, term}`.

## Examples

    # Transform the result after action
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:calculate_stats, %{data: [1, 2, 3]})
    ...> |> Ash.ActionInput.after_action(fn input, result ->
    ...>   enhanced_result = Map.put(result, :calculated_at, DateTime.utc_now())
    ...>   {:ok, enhanced_result}
    ...> end)

    # Log successful actions
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:important_action, %{})
    ...> |> Ash.ActionInput.after_action(fn inp, result ->
    ...>   Logger.info("Action completed successfully")
    ...>   {:ok, result}
    ...> end)

    # Return notifications
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:notify_users, %{message: "Hello"})
    ...> |> Ash.ActionInput.after_action(fn input, result ->
    ...>   notification = %Ash.Notifier.Notification{
    ...>     resource: input.resource,
    ...>     action: input.action,
    ...>     data: result
    ...>   }
    ...>   {:ok, result, [notification]}
    ...> end)

    # Handle errors
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:risky_action, %{})
    ...> |> Ash.ActionInput.after_action(fn input, result ->
    ...>   if is_error_result?(result) do
    ...>     {:error, "Action failed with custom error"}
    ...>   else
    ...>     {:ok, result}
    ...>   end
    ...> end)

## See also

- `before_action/3` for hooks that run before the action executes
- `for_action/4` for creating action inputs
- `Ash.run_action/2` for executing the action with the input

# `after_transaction`

```elixir
@spec after_transaction(
  input :: t(),
  fun :: after_transaction_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds an after transaction hook to the action input.

After transaction hooks are executed after the transaction completes, regardless of success or failure.
They receive both the input and the transaction result, and can modify the result.

> #### Actions without a return type {: .tip}
>
> After transaction hooks will work for generic actions without a return
> type, however they will receive `:ok | {:error, term}` as their `result`
> argument and are expected to return `:ok | {:error, term}`.

## Examples

    # Add cleanup after transaction
    iex> input
    ...> |> Ash.ActionInput.after_transaction(fn input, result ->
    ...>   cleanup_resources()
    ...>   result
    ...> end)

## See also

- `before_transaction/2` for hooks that run before the transaction
- `around_transaction/2` for hooks that wrap the entire transaction
- `after_action/2` for hooks that run after the action (inside transaction)

# `around_transaction`

```elixir
@spec around_transaction(
  input :: t(),
  fun :: around_transaction_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds an around transaction hook to the action input.

Around transaction hooks wrap the entire transaction execution. They receive a callback
function that they must call to execute the transaction, allowing them to add logic
both before and after the transaction.

> #### Actions without a return type {: .tip}
>
> Around transaction hooks will work for generic actions without a return
> type, however they will receive `:ok` as the result of the callback
> instead of `{:ok, result}`. They are expected to return
> `:ok | {:error, any}`.

## Examples

    # Add retry logic around transaction
    iex> input
    ...> |> Ash.ActionInput.around_transaction(fn input, callback ->
    ...>   case callback.(input) do
    ...>     {:ok, result} -> {:ok, result}
    ...>     {:error, %{retryable?: true}} -> callback.(input) # Retry once
    ...>     error -> error
    ...>   end
    ...> end)

## See also

- `before_transaction/2` for hooks that run before the transaction
- `after_transaction/2` for hooks that run after the transaction
- `before_action/3` and `after_action/2` for hooks that run inside the transaction

# `before_action`

```elixir
@spec before_action(
  input :: t(),
  fun :: before_action_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds a before_action hook to the action input.

Before action hooks are called with the action input and can modify it before
the action executes. They can also add errors to halt processing or return
notifications to be processed later.

## Examples

    # Validate arguments before action
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_notification, %{message: "Hello"})
    ...> |> Ash.ActionInput.before_action(fn input ->
    ...>   if String.length(input.arguments.message) > 100 do
    ...>     Ash.ActionInput.add_error(input, "Message too long")
    ...>   else
    ...>     input
    ...>   end
    ...> end)

    # Set computed arguments
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:process_data, %{data: "test"})
    ...> |> Ash.ActionInput.before_action(fn input ->
    ...>   Ash.ActionInput.set_argument(input, :processed_at, DateTime.utc_now())
    ...> end)

    # Return notifications
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:audit_action, %{})
    ...> |> Ash.ActionInput.before_action(fn input ->
    ...>   notification = %Ash.Notifier.Notification{
    ...>     resource: input.resource,
    ...>     action: input.action,
    ...>     data: %{audit: "before_action"}
    ...>   }
    ...>   {input, %{notifications: [notification]}}
    ...> end)

## Options

- `prepend?` - If `true`, adds the hook to the beginning of the list instead of the end

## See also

- `after_action/2` for hooks that run after the action completes
- `for_action/4` for creating action inputs
- `add_error/2` for adding validation errors in hooks

# `before_transaction`

```elixir
@spec before_transaction(
  input :: t(),
  fun :: before_transaction_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds a before transaction hook to the action input.

Before transaction hooks are executed before the transaction begins (if the action is transactional).
They can modify the action input or halt execution by returning an error.

## Examples

    # Add logging before transaction
    iex> input
    ...> |> Ash.ActionInput.before_transaction(fn input ->
    ...>   IO.puts("Starting transaction for action")
    ...>   input
    ...> end)

## See also

- `after_transaction/2` for hooks that run after the transaction
- `around_transaction/2` for hooks that wrap the entire transaction
- `before_action/3` for hooks that run before the action (inside transaction)

# `delete_argument`

```elixir
@spec delete_argument(
  input :: t(),
  argument_or_arguments :: atom() | String.t() | [atom() | String.t()]
) :: t()
```

Deletes one or more arguments from the subject.

## Parameters

  * `subject` - The subject to delete arguments from
  * `arguments` - Single argument name or list of argument names to delete

# `fetch_argument`

```elixir
@spec fetch_argument(t(), atom() | String.t()) :: {:ok, term()} | :error
```

Fetches the value of an argument provided to the input.

Returns `{:ok, value}` if the argument exists, or `:error` if not found.
This is the safer alternative to `get_argument/2` when you need to distinguish
between a `nil` value and a missing argument.

## Examples

    # Fetch an argument that exists
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_notification, %{priority: :high})
    ...> |> Ash.ActionInput.fetch_argument(:priority)
    {:ok, :high}

    # Fetch an argument that doesn't exist
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_notification, %{})
    ...> |> Ash.ActionInput.fetch_argument(:missing_arg)
    :error

    # Distinguish between nil and missing arguments
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:example, %{optional_field: nil})
    ...> |> Ash.ActionInput.fetch_argument(:optional_field)
    {:ok, nil}

    # Use in conditional logic
    iex> input = MyApp.Post |> Ash.ActionInput.for_action(:process, %{})
    iex> case Ash.ActionInput.fetch_argument(input, :mode) do
    ...>   {:ok, mode} -> "Processing in #{mode} mode"
    ...>   :error -> "Using default processing mode"
    ...> end
    "Using default processing mode"

## See also

- `get_argument/2` for simpler argument access
- `set_argument/3` for setting argument values
- `for_action/4` for providing initial arguments

# `for_action`

```elixir
@spec for_action(
  resource_or_input :: Ash.Resource.t() | t(),
  action :: atom(),
  params :: map(),
  opts :: Keyword.t()
) :: t()
```

Creates a new input for a generic action.

This is the primary way to create action inputs for generic actions. It validates
the action exists, sets up the input with proper defaults, and validates any
provided arguments according to the action's argument definitions.

## Examples

    # Create input for a simple action
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_notification, %{message: "Hello"})
    %Ash.ActionInput{action: %{name: :send_notification}, arguments: %{message: "Hello"}, ...}

    # Create input with options
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:complex_action, %{data: "test"},
    ...>   actor: current_user, authorize?: true)
    %Ash.ActionInput{arguments: %{data: "test"}, ...}

    # Create input and then modify it
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.for_action(:example, %{})
    ...> |> Ash.ActionInput.set_context(%{source: "api"})
    iex> input.action.name
    :example

## Options

* `:domain` (`Ash.Domain`) - The domain to use for the action. The resource's domain is used by default.

* `:context` (`t:map/0`) - Context to set on the action input. The default value is `%{}`.

* `:authorize?` - Whether or not to run authorization on the action. Default behavior of this option is controlled by the domain.

* `:tenant` (`t:term/0`) - The tenant to use for the action.

* `:scope` (`t:term/0`) - A value that implements the `Ash.Scope.ToOpts` protocol, for passing around actor/tenant/context in a single value. See `Ash.Scope.ToOpts` for more.

* `:actor` (`t:term/0`) - The actor performing the action

* `:skip_unknown_inputs` - A list of unknown inputs to skip. Use `:*` to skip all unknown inputs.

* `:tracer` (`t:term/0`) - A tracer or list of tracers to trace action execution.

* `:private_arguments` (`t:map/0`) - A list of private arguments to be set before the action is invoked. The default value is `%{}`.

* `:load` (`t:term/0`) - A load statement to apply on the resulting records after the action is invoked.

## See also

- `new/2` for creating basic inputs
- `set_argument/3` for adding arguments after creation
- `Ash.run_action/2` for executing the action with the input
- `d:Ash.Resource.Dsl.actions.action` for defining generic actions
- [Generic Actions Guide](/documentation/topics/actions/generic-actions.md) for understanding generic actions
- [Actions Guide](/documentation/topics/actions/actions.md) for general action concepts

# `get_argument`

```elixir
@spec get_argument(t(), atom() | String.t()) :: term()
```

Gets the value of an argument provided to the input.

Returns the argument value if found, or `nil` if not found. Arguments are
validated and cast according to the action's argument definitions when set.

## Examples

    # Get an argument that exists
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_email, %{recipient: "user@example.com"})
    ...> |> Ash.ActionInput.get_argument(:recipient)
    "user@example.com"

    # Get an argument that doesn't exist returns nil
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_email, %{})
    ...> |> Ash.ActionInput.get_argument(:missing_arg)
    nil

    # Arguments can be accessed by string or atom key
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:example, %{"message" => "hello"})
    ...> |> Ash.ActionInput.get_argument(:message)
    "hello"

## See also

- `fetch_argument/2` for safer argument access with explicit error handling
- `set_argument/3` for setting argument values
- `for_action/4` for providing initial arguments

# `load`

```elixir
@spec load(t(), term()) :: t()
```

Calls the provided load statement on the result of the action at the very end of the action.

This effectively will call `Ash.load/3` on the result of the action after all hooks,
and assumes the result of the action will be compatible with said load call.

## Examples

    # Load a single relationship
    iex> input = Ash.ActionInput.for_action(MyApp.Post, :publish, %{})
    iex> input = Ash.ActionInput.load(input, :comments)
    iex> input.load
    [:comments]

    # Load multiple relationships
    iex> input = Ash.ActionInput.for_action(user, :deactivate, %{reason: "user request"})
    iex> input = Ash.ActionInput.load(input, [:posts, :profile])
    iex> input.load
    [:posts, :profile]

    # Load nested relationships
    iex> input = Ash.ActionInput.for_action(MyApp.Comment, :approve, %{approved_by: "admin"})
    iex> input = Ash.ActionInput.load(input, [post: [:author, :comments]])
    iex> input.load
    [[post: [:author, :comments]]]

    # Chain multiple load calls (they accumulate)
    iex> input = Ash.ActionInput.for_action(post, :archive, %{})
    iex> input = input
    ...> |> Ash.ActionInput.load(:author)
    ...> |> Ash.ActionInput.load(:comments)
    iex> input.load
    [:author, :comments]

## See also

- `loading?/2` for checking if something is being loaded
- `Ash.Changeset.load/2` for loading in changesets
- `Ash.Query.load/2` for loading in queries

# `loading?`

Returns true if the field/relationship or path to field/relationship is being loaded at the very end of the action.

Note that this is only in regards to the `:load` option (See `load/2`). This function does not indicate anything about the loading behavior of the action's run function.

It accepts an atom or a list of atoms, which is treated for as a "path", i.e:

    Resource |> Ash.ActionInput.load(friends: [enemies: [:score]]) |> Ash.ActionInput.loading?([:friends, :enemies, :score])
    iex> true

    Resource |> Ash.ActionInput.load(friends: [enemies: [:score]]) |> Ash.ActionInput.loading?([:friends, :score])
    iex> false

    Resource |> Ash.ActionInput.load(friends: [enemies: [:score]]) |> Ash.ActionInput.loading?(:friends)
    iex> true

# `new`

```elixir
@spec new(Ash.Resource.t(), Ash.Domain.t() | nil) :: t()
```

Creates a new action input from a resource.

This creates a basic action input struct that can be used as a starting point
for building inputs for generic actions. Use `for_action/4` to create an input
bound to a specific action.

## Examples

    # Create a new action input for a resource
    iex> Ash.ActionInput.new(MyApp.Post)
    %Ash.ActionInput{resource: MyApp.Post, domain: nil, ...}

    # Usually you'll want to use for_action/4 instead
    iex> MyApp.Post |> Ash.ActionInput.for_action(:send_notification, %{message: "Hello"})
    %Ash.ActionInput{action: %{name: :send_notification}, arguments: %{message: "Hello"}, ...}

## See also

- `for_action/4` for creating action-specific inputs
- `set_argument/3` for adding arguments
- `set_context/2` for adding context

# `set_argument`

```elixir
@spec set_argument(input :: t(), name :: atom() | String.t(), value :: term()) :: t()
```

Sets an argument value on the action input.

The argument value is validated and cast according to the action's argument
definition. If validation fails, errors will be added to the input and it
will be marked as invalid.

## Examples

    # Set a simple argument
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_notification, %{})
    ...> |> Ash.ActionInput.set_argument(:message, "Hello World")
    ...> |> Ash.ActionInput.get_argument(:message)
    "Hello World"

    # Set multiple arguments by chaining
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.for_action(:complex_action, %{})
    ...> |> Ash.ActionInput.set_argument(:priority, :high)
    ...> |> Ash.ActionInput.set_argument(:batch_size, 100)
    iex> Ash.ActionInput.get_argument(input, :priority)
    :high

    # Arguments are validated according to type
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.for_action(:schedule_job, %{})
    ...> |> Ash.ActionInput.set_argument(:run_at, ~U[2024-01-01 10:00:00Z])
    iex> input.valid?
    true

## See also

- `get_argument/2` for retrieving argument values
- `set_private_argument/3` for setting private arguments
- `for_action/4` for providing initial arguments

# `set_context`

```elixir
@spec set_context(t(), map() | nil) :: t()
```

Deep merges the provided map into the input context.

Context is used to pass additional information through the action pipeline
that can be accessed by action implementations, changes, and validations.
The context is merged deeply, so nested maps will be combined rather than replaced.

Do not use the `private` key in your custom context, as that is reserved for
internal use.

## Examples

    # Set simple context values
    iex> MyApp.Post
    ...> |> Ash.ActionInput.new()
    ...> |> Ash.ActionInput.set_context(%{source: "api", user_id: 123})
    ...> |> then(& &1.context.source)
    "api"

    # Context is merged deeply
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.new()
    ...> |> Ash.ActionInput.set_context(%{metadata: %{version: 1}})
    ...> |> Ash.ActionInput.set_context(%{metadata: %{trace_id: "abc123"}})
    iex> input.context.metadata
    %{version: 1, trace_id: "abc123"}

    # Use context in action implementations
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:process_data, %{data: "test"})
    ...> |> Ash.ActionInput.set_context(%{
    ...>   request_id: "req_456",
    ...>   feature_flags: %{new_algorithm: true}
    ...> })

## See also

- `for_action/4` for setting context when creating inputs
- Action implementations can access context for custom logic
- `set_tenant/2` for tenant-specific context

# `set_private_argument`

```elixir
@spec set_private_argument(input :: t(), name :: atom(), value :: term()) :: t()
```

Sets a private argument value on the action input.

Private arguments are not exposed in the public API and can only be set
internally. This function will only work for arguments marked as `public?: false`
in the action definition.

## Examples

    # Set a private argument (assuming :internal_flag is private)
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:example, %{})
    ...> |> Ash.ActionInput.set_private_argument(:internal_flag, true)
    ...> |> Ash.ActionInput.get_argument(:internal_flag)
    true

    # Attempting to set a public argument as private will error
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.for_action(:example, %{})
    ...> |> Ash.ActionInput.set_private_argument(:public_arg, "value")
    iex> input.valid?
    false

    # Use in action implementations for internal state
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:complex_workflow, %{data: "user_data"})
    ...> |> Ash.ActionInput.set_private_argument(:workflow_step, 1)

## See also

- `set_argument/3` for setting public arguments
- `get_argument/2` for retrieving argument values
- Action argument definitions with `public?: false`

# `set_tenant`

```elixir
@spec set_tenant(t(), Ash.ToTenant.t()) :: t()
```

Sets the tenant to use when calling the action.

In multitenant applications, this configures which tenant's data the action
should operate on. The tenant value is used for data isolation and access control.

## Examples

    # Set tenant using a string identifier
    iex> MyApp.Post
    ...> |> Ash.ActionInput.new()
    ...> |> Ash.ActionInput.set_tenant("org_123")
    ...> |> then(& &1.tenant)
    "org_123"

    # Set tenant using a struct that implements Ash.ToTenant
    iex> org = %MyApp.Organization{id: 456}
    iex> input = MyApp.Post
    ...> |> Ash.ActionInput.for_action(:send_notification, %{})
    ...> |> Ash.ActionInput.set_tenant(org)
    iex> input.tenant == org
    true

    # Use with action execution
    iex> MyApp.Post
    ...> |> Ash.ActionInput.for_action(:cleanup, %{})
    ...> |> Ash.ActionInput.set_tenant("tenant_456")
    ...> |> Ash.run_action()

## See also

- `for_action/4` for setting tenant when creating inputs
- `Ash.ToTenant` protocol for custom tenant conversion
- `set_context/2` for adding tenant to action context

---

*Consult [api-reference.md](api-reference.md) for complete listing*
