I think I understand what is happening.
If I use setTag()/setCustomContext()/setUserContext() in my server.js before all my code, they all return false, probably because the transaction hasn't started I guess ?
If I set them in my routes handlers it works.
But what If I want to set them for all future transactions ? Am I misunderstanding something on the transactions behaviors ?
EDIT :
For now im using the hapi function "server.ext()" a middleware that is loaded before all my routes handlers to set contexts to the APM transaction and it works fine. I noticed that the captureBody still doesn't work.
Here is my server.js file :
const apm = require('elastic-apm-node').start({
  // active: process.env.NODE_ENV === 'production'
  serviceName: 'api-test',
  logLevel: 'debug',
  filterHttpHeaders: false,
  captureBody: 'all',
  serverUrl: 'http://logs.mydomain.net:8200',
});
const Hapi = require('hapi');
const Inert = require('inert');
const Vision = require('vision');
const hapiswaggerd = require('hapi-swaggered');
const hapiswaggeredui = require('hapi-swaggered-ui');
const Hapijwt = require('hapi-auth-jwt2');
const hapiI18n = require('hapi-basic-i18n');
const Path = require('path');
require('dotenv').config({ silent: true, path: Path.join(__dirname, '.env') });
const good = require('good');
const routes = require('./routes');
const jwtService = require('./jwt/jwt.service');
const server = new Hapi.Server({ debug: { request: ['error'] } });
server.connection({ port: process.env.PORT || 8080, host: '0.0.0.0', routes: { cors: true } });
server.register([
  Inert,
  Vision,
  {
    register: hapiI18n,
    options: {
      locale_path: Path.join(__dirname, './assets/i18n/'),
      default_language: 'FR',
      available_languages: ['FR'],
    },
  },
  {
    register: hapiswaggerd,
    options: {
      endpoint: '/swagger-doc',
    },
  },
  {
    register: Hapijwt,
  },
  {
    register: hapiswaggeredui,
    options: {
      title: 'Title API',
      path: '/documentation',
      authorization: { // see above
        field: 'Authorization',
        scope: 'header',
        placeholder: 'Enter your jwt token here',
      },
      swaggerOptions: {},
    },
  },
  {
    register: good,
    options: {
      ops: {
        interval: 1000,
      },
      reporters: {
        console: [{
          module: 'good-squeeze',
          name: 'Squeeze',
          args: [{
            response: '*',
            error: '*',
          }],
        },
        {
          module: 'good-console',
        }, 'stdout'],
      },
    },
  },
], (err) => {
  server.auth.strategy('jwt', 'jwt', {
    key: jwtService.getAppSecrets,
    validateFunc: (decoded, request, callback) => {
      if (!decoded) {
        return callback(null, false);
      }
      const scope = [];
      if (decoded) {
        if (decoded.role) {
          scope.push(decoded.role);
        }
        if (decoded.currentOrganisation && decoded.currentOrganisation.role) {
          scope.push(decoded.currentOrganisation.role);
        }
      }
      // Scope is an array that contains AppRole and OrganisationRole
      return callback(null, true, Object.assign(decoded, { scope }));
    },
    verifyOptions: { algorithms: ['HS256'] },
  });
  if (err) throw err;
  routes(server, apm);
  server.start((error) => {
    if (error) {
      throw error;
    }
    server.ext('onPreHandler', (request, reply) => {
      apm.setCustomContext({ jwtContent: request.auth.credentials });
      apm.setUserContext({
        id: request.auth.credentials.id,
        email: request.auth.credentials.email,
        username: `${request.auth.credentials.firstname} ${request.auth.credentials.lastname}`,
      });
      return reply.continue();
    });
  });
});
module.exports = server;