> For the complete documentation index, see [llms.txt](https://docs.nighty.one/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nighty.one/custom-features-tab/custom-parameters.md).

# Custom Parameters

On [Understanding Events and Parameters Page](/custom-features-tab/understanding-events-and-parameters.md) you learned that every event hands you a set of parameters automatically. But sometimes the thing you need simply isn't one of them.

**Custom parameters let you add your own.**

## When you need one

Here's the clearest example.

You want to post an automated message in a specific channel every hour. So you pick the **Repeat Action** event… and you have nothing to work with. Repeat Action isn't triggered by anything on Discord - nobody sent a message, nobody joined a server - so there's no `channel`, no `user`, no `guild`. The event carries no parameters at all.

<figure><img src="/files/L6vS0F7GZ4nPWxpohdMB" alt=""><figcaption></figcaption></figure>

But you *need* a channel. Specifically, the one channel you want to post in.

That's what a custom parameter is for: **you tell Nighty which channel you mean, and it goes and gets it for you.**

The same applies any time you need something the event didn't hand you:

* Post in a **different** channel than the one the event happened in
* Check a role in **another** server
* Read a specific pinned message that has nothing to do with the current event
* Look up a specific person who isn't involved in what just happened

## Adding one

You add custom parameters in the **parameters** section of your feature. The popup asks you for a few things:

{% stepper %}
{% step %}

## The type

<figure><img src="/files/YAGJVEcfbPRfZkQHd9QA" alt=""><figcaption></figcaption></figure>

What kind of Discord thing is it? Your choice here decides which IDs Nighty needs from you.
{% endstep %}

{% step %}

## A name

This is how you'll refer to it everywhere else in the feature, so make it obvious to your future self. `general_channel` is good. `chan2` is not.
{% endstep %}

{% step %}

## A description (Refers To)

What this parameter is for. You'll thank yourself in three months.

> **Example**
>
> Name: `general_channel`\
> Refers to: *the channel I want to send automated messages in*
> {% endstep %}

{% step %}

## Availability

*When* Nighty should go and fetch it. This needs its own explanation - see [Availability — when Nighty goes and gets it](#availability--when-nighty-goes-and-gets-it).
{% endstep %}
{% endstepper %}

## How a parameter is actually obtained

To understand availability, you first need to know what "obtaining" means.

**Discord doesn't tell your app everything up front.**

When you launch Discord - or Nighty - it does **not** download every member of every server, every channel, and every message. That would be enormous. Instead it starts with very little and **learns as it goes**:

* Someone sends a message → now it knows about that person and that channel
* You open a channel → now it knows that channel and the recent messages in it
* You scroll the member list → now it knows those members

Everything it has learned is kept in a **cache** -a short-term memory of things it has seen recently.

So when your feature says *"give me the channel with this ID"*, one of two things happens:

| Situation                  | What happens                                                                                                        |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| ✅ **Already in the cache** | Instant. Nighty already knows it. Costs nothing.                                                                    |
| ⏳ **Not in the cache**     | Nighty has to **ask Discord**: *"tell me about the channel with this ID."* That's a real request over the internet. |

That second case is the important one. Asking Discord:

* **takes time** - it's a round trip to Discord's servers
* **counts against your rate limits** - Discord only lets you ask so many questions so quickly

None of this is a problem when it happens occasionally. It becomes a problem when it happens on **every single message in every server you're in**.

Which is exactly what availability controls.

{% hint style="warning" %}
**Wrong IDs get given up on.** If Nighty fails to fetch the same ID **3 times**, it stops trying that ID entirely and your parameter will just be empty from then on. This protects you from a typo generating endless failed requests. If you fix a wrong ID, **restart Nighty** so it forgets the failures and tries again.
{% endhint %}

## Availability - when Nighty goes and gets it

Availability is the point in your feature where Nighty asks Discord for the parameter. There are three choices:

| Setting                                | Fetched                                                        | Usable in                                 |
| -------------------------------------- | -------------------------------------------------------------- | ----------------------------------------- |
| **Everywhere**                         | Fetched as soon as the event fires, on **every** event, always | Everything (Filters, Conditions, Actions) |
| **Conditions and actions**             | Only if your filters passed                                    | Conditions and actions                    |
| **Actions** (final actions block only) | Only if your filters **and** conditions passed                 | Actions only                              |

Think of it as three gates. The later you set it, the more events have already been thrown away before Nighty bothers to fetch anything.

### Why "as late as possible" is the rule

Picture a feature on **New Message** in a busy server. A thousand messages an hour come through. Your filters and conditions narrow that down to the five you actually care about.

| Availability | Requests to Discord per hour                                  |
| ------------ | ------------------------------------------------------------- |
| Earliest     | **1000** - one for every message, even the 995 you threw away |
| Latest       | **5** - only the ones that got all the way through            |

Same feature, same result, 200× less work.

{% hint style="success" %}
**The rule: pick the latest option that still works.**

If you only use the parameter in your actions, choose **actions only**. If a condition needs to check it, choose **after filters**. Only choose the earliest option if you genuinely have no other choice.
{% endhint %}

{% hint style="info" %}
In reality there are some safety checks behind the scenes not allowing Nighty to make that many requests to Discord, but you should still use the "as late as possible" principle.
{% endhint %}

### When you *do* need it earlier

Move it earlier only when something earlier in the chain needs it:

* **A condition uses it** → it must be fetched **after filters** at the latest
* **Nothing else uses it** → **actions only**

There's one thing custom parameters can **never** be used for, no matter what you pick: **filters**. Filters run before any custom parameter has been fetched, so they can only use the event's own built-in parameters.

## Worked examples

### Automated hourly message in one channel

* Event: Repeat Action
* Custom parameter: type **Channel**, name `announcement_channel`, the channel's ID
* Availability: **actions only** - nothing else needs it
* Action: send a message to `announcement_channel`

### Log deleted messages to a private channel

* Event: Message Delete
* Custom parameter: type **Channel**, name `log_channel`, your private channel's ID
* Availability: **actions only**
* Action: send the deleted message's details to `log_channel`

Notice the pattern: whenever the parameter is only used at the very end, it's set to **actions only**. That's the default you should reach for.

## A note on the Custom Feature event

There's one case where a custom parameter arrives **without** being fetched at all: the **Custom Feature** event, where another feature hands parameters over to this one directly.

In that case the thing has already been loaded by the feature that sent it, so there's no ID to provide and no request to Discord.
