diff --git a/docs/docs/noir_js/reference/01_noirjs.md b/docs/docs/noir_js/reference/01_noirjs.md
index d9e5a0c6115..0d6d5abbbff 100644
--- a/docs/docs/noir_js/reference/01_noirjs.md
+++ b/docs/docs/noir_js/reference/01_noirjs.md
@@ -58,10 +58,12 @@ await noirInstance.init();
 
 This async method allows to execute a circuit to get its witness and return value. [`generateFinalProof`](#generatefinalproof) calls it for you, but you can call it directly (i.e. to feed directly to a backend, or to get the return value).
 
+You can optionally provide a foreignCallHandler, to handle functions that should run outside of the prover (e.g. `std::println`)
+
 ### Syntax
 
 ```js
-async execute(inputs)
+async execute(inputs, foreignCallHandler)
 ```
 
 ### Parameters
@@ -69,6 +71,7 @@ async execute(inputs)
 | Parameter | Type   | Description                                      |
 | --------- | ------ | ------------------------------------------------ |
 | `inputs`   | Object | An object containing the inputs to your circuit. |
+| `foreignCallHandler` (optional) | Function | A function handling the foreign call from your circuit |
 
 ### Returns
 
@@ -81,6 +84,7 @@ async execute(inputs)
 
 ```js
 const { witness, returnValue } = await noir.execute(inputs)
+const { witness, returnValue } = await noir.execute(inputs, (name, args) => console.log(`Received foreign call ${name} with arguments ${args}`))
 ```
 
 ## `generateFinalProof`
diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts
index bf48e15fcad..cfd7a715dfe 100644
--- a/tooling/noir_js/src/program.ts
+++ b/tooling/noir_js/src/program.ts
@@ -2,7 +2,7 @@
 import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types';
 import { generateWitness } from './witness_generation.js';
 import initAbi, { abiDecode, InputMap, InputValue } from '@noir-lang/noirc_abi';
-import initACVM, { compressWitness } from '@noir-lang/acvm_js';
+import initACVM, { compressWitness, ForeignCallHandler } from '@noir-lang/acvm_js';
 
 export class Noir {
   constructor(
@@ -29,9 +29,12 @@ export class Noir {
   }
 
   // Initial inputs to your program
-  async execute(inputs: InputMap): Promise<{ witness: Uint8Array; returnValue: InputValue }> {
+  async execute(
+    inputs: InputMap,
+    foreignCallHandler?: ForeignCallHandler,
+  ): Promise<{ witness: Uint8Array; returnValue: InputValue }> {
     await this.init();
-    const witness = await generateWitness(this.circuit, inputs);
+    const witness = await generateWitness(this.circuit, inputs, foreignCallHandler);
     const { return_value: returnValue } = abiDecode(this.circuit.abi, witness);
     return { witness: compressWitness(witness), returnValue };
   }
diff --git a/tooling/noir_js/src/witness_generation.ts b/tooling/noir_js/src/witness_generation.ts
index f96cddb0eca..e3ddb1a2a21 100644
--- a/tooling/noir_js/src/witness_generation.ts
+++ b/tooling/noir_js/src/witness_generation.ts
@@ -1,19 +1,25 @@
 import { abiEncode, InputMap } from '@noir-lang/noirc_abi';
 import { base64Decode } from './base64_decode.js';
-import { executeCircuit, WitnessMap } from '@noir-lang/acvm_js';
+import { executeCircuit, WitnessMap, ForeignCallHandler, ForeignCallInput } from '@noir-lang/acvm_js';
 import { CompiledCircuit } from '@noir-lang/types';
 
+const defaultForeignCallHandler: ForeignCallHandler = (name: string, args: ForeignCallInput[]) => {
+  throw Error(`Unexpected oracle during execution: ${name}(${args.join(', ')})`);
+};
+
 // Generates the witnesses needed to feed into the chosen proving system
-export async function generateWitness(compiledProgram: CompiledCircuit, inputs: InputMap): Promise<WitnessMap> {
+export async function generateWitness(
+  compiledProgram: CompiledCircuit,
+  inputs: InputMap,
+  foreignCallHandler: ForeignCallHandler = defaultForeignCallHandler,
+): Promise<WitnessMap> {
   // Throws on ABI encoding error
   const witnessMap = abiEncode(compiledProgram.abi, inputs);
 
   // Execute the circuit to generate the rest of the witnesses and serialize
   // them into a Uint8Array.
   try {
-    const solvedWitness = await executeCircuit(base64Decode(compiledProgram.bytecode), witnessMap, () => {
-      throw Error('unexpected oracle during execution');
-    });
+    const solvedWitness = await executeCircuit(base64Decode(compiledProgram.bytecode), witnessMap, foreignCallHandler);
     return solvedWitness;
   } catch (err) {
     throw new Error(`Circuit execution failed: ${err}`);