Aside from a certain trick I won't get into now, Velocity *can* process Program Token content -- if the other tokens are also Velocity tokens! This is why I use VTL in lieu of text tokens much of the time.

BUT! The problem is the temporary tokens you can pass via API don't support VTL. Whether an oversight or a bug, this is the main problem in your scenario.

If you can update a VTL token first using the Asset API, that works, but not all apps will work with this sort of timing.

One example is including data (non-output) {{my.dataToken}} like this in the <head>:

#set( $language = "en-us" )

Then you can reference any of its variables in a subsequent {{my.outputToken}}:

Your language is ${language}!

This works because Velocity uses a persistent (i.e. reusable/reentrant) global context. Each time you reenter the Velocity world, you see all its variables.

I use this approach almost all the time, where data tokens are kept separate from output tokens. You can't make Velocity into a pretty language, but you can make it a lot like a real language in terms of separation of concerns, etc.

I thought I'd be able to use Velocity, but it seems like this is impossible - we can't check the values of the program tokens from inside the email script, which significantly reduces the value of being able to programatically trigger emails.

Am I missing something here? If I've read this thread correctly, there's no way to get this doing what I want. Or is there some newer, fancier solution that can make this work?

I thought I'd be able to use Velocity, but it seems like this is impossible - we can't check the values of the program tokens from inside the email script, which significantly reduces the value of being able to programatically trigger emails.

That's correct. You can't pass Velocity tokens as "runtime tokens" -- those {{my.token}} values that only exist in the context of a "Request Campaign" execution. Only standard token types work there.

Workarounds to do what you describe are:

do whatever display logic you would've done in Velocity in your client app instead, then pass a text {{my.token}}

set a textarea field on the lead to contain error block, then trigger on Data Value Changed to launch the campaign -- keeping the obvious race condition in mind since different runs won't be siloed from one another