diff --git a/src/box/call.c b/src/box/call.c
index ce55d04e1b24abe0b6524d14c4019ba1aaf2d895..65312e2406c21cf20dbe29daa8cc457dfa595300 100644
--- a/src/box/call.c
+++ b/src/box/call.c
@@ -41,8 +41,11 @@
 #include "iproto_constants.h"
 #include "rmean.h"
 #include "small/obuf.h"
+#include "small/rlist.h"
 #include "tt_static.h"
 
+struct rlist box_on_call = RLIST_HEAD_INITIALIZER(box_on_call);
+
 static const struct port_vtab port_msgpack_vtab;
 
 void
@@ -131,6 +134,23 @@ box_module_reload(const char *name)
 	return schema_module_reload(name, name + strlen(name));
 }
 
+/** Runs box_on_call triggers. */
+static inline void
+box_run_on_call(enum iproto_type type, const char *expr, int expr_len,
+		const char *args)
+{
+	assert(type == IPROTO_CALL || type == IPROTO_EVAL);
+	if (likely(rlist_empty(&box_on_call)))
+		return;
+	struct box_on_call_ctx ctx = {
+		.is_eval = (type == IPROTO_EVAL),
+		.expr = expr,
+		.expr_len = expr_len,
+		.args = args,
+	};
+	trigger_run(&box_on_call, &ctx);
+}
+
 int
 box_process_call(struct call_request *request, struct port *port)
 {
@@ -148,6 +168,7 @@ box_process_call(struct call_request *request, struct port *port)
 	if (func != NULL) {
 		if (func_access_check(func) != 0)
 			return -1;
+		box_run_on_call(IPROTO_CALL, name, name_len, request->args);
 		if (func_call_no_access_check(func, &args, port) != 0)
 			return -1;
 	} else {
@@ -155,6 +176,7 @@ box_process_call(struct call_request *request, struct port *port)
 						 SC_FUNCTION,
 						 tt_cstr(name, name_len)) != 0)
 			return -1;
+		box_run_on_call(IPROTO_CALL, name, name_len, request->args);
 		if (box_lua_call(name, name_len, &args, port) != 0)
 			return -1;
 	}
@@ -173,6 +195,7 @@ box_process_eval(struct call_request *request, struct port *port)
 			    request->args_end - request->args);
 	const char *expr = request->expr;
 	uint32_t expr_len = mp_decode_strl(&expr);
+	box_run_on_call(IPROTO_EVAL, expr, expr_len, request->args);
 	if (box_lua_eval(expr, expr_len, &args, port) != 0)
 		return -1;
 	return 0;
diff --git a/src/box/call.h b/src/box/call.h
index 45580bc9d1c28cbe0601717f6d9604e65e0648c2..0f8e31842ce1d5dc660354a45ba43d8b5f9022ce 100644
--- a/src/box/call.h
+++ b/src/box/call.h
@@ -31,6 +31,10 @@
  * SUCH DAMAGE.
  */
 
+#include <stdbool.h>
+
+#include "small/rlist.h"
+
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
@@ -38,6 +42,24 @@ extern "C" {
 struct port;
 struct call_request;
 
+/** Context passed to box_on_call trigger callback. */
+struct box_on_call_ctx {
+	/** True for EVAL, false for CALL. */
+	bool is_eval;
+	/** CALL function name or EVAL expression. */
+	const char *expr;
+	/** Length of the expr string. */
+	int expr_len;
+	/** Arguments (MsgPack array). */
+	const char *args;
+};
+
+/**
+ * Triggers invoked by box_process_call and box_process_eval.
+ * Trigger callback is passed on_box_call_ctx.
+ */
+extern struct rlist box_on_call;
+
 /**
  * Reload loadable module by name.
  *