Ghost Admin webhooks API return 404

Hello,

I am hosting a blog at https://blog.kop.ax

I want to create webhook using the admin API. After I generate the encrypted key, I successfully call the route /ghost/api/v3/admin/site and get the site from an admin API endpoint.

However, when I call the /webhooks GET or POST I keep having 404 response.

I’ve tripled checked, what am I doing wrong ?

Hey @Dimitri_Kopriwa . Would need more details to be able to help here. What version of Ghost are you running? What kind of client are you using to call the /webhooks endpoint? Are you using recommended Admin-API-SDK?

Hi @naz and thanks for your quick reply as I am getting a bit tired not being able to sort it out.

I use the latest version of ghost:3.41.3-alpine running with Docker 5:19.03.13~3-0~debian-buster.

I have a retrofit HTTP client (Java) and I use com.auth0:java-jwt:3.13.0 to sign the JWT.

My configuration is as follow :

First, I want to say I’ve read multiple times :

My server is in Java, and this is my snippet:

public GhostBlogAdminApiService createAdminService(GhostBlog blog) throws DecoderException {
    String adminApiKeId = blog.getAdminApiKey().split(":")[0];
    String adminApiKeySecret = blog.getAdminApiKey().split(":")[1];
    byte[] keyBytes = Hex.decodeHex(adminApiKeySecret.toCharArray());
    Algorithm algorithm = Algorithm.HMAC256(keyBytes);
    Map<String, Object> headerClaims = new HashMap<>();
    headerClaims.put("typ", "JWT");
    headerClaims.put("alg", "HS256");
    headerClaims.put("kid", adminApiKeId);
    Date now = new Date();
    Date in5min = DateUtils.addMinutes(now, 5);
    String token = JWT.create()
            .withHeader(headerClaims)
            .withIssuer("auth0")
            .withIssuedAt(now)
            .withExpiresAt(in5min)
            .withAudience("/v3/admin/")
            .sign(algorithm);
    try {
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("auth0")
                .build(); // Reusable verifier instance
        DecodedJWT jwt = verifier.verify(token);
        System.out.println(jwt.toString()); // <--- I always verify the signature without exception
    } catch (JWTVerificationException exception){
        // Invalid signature/claims
    }
    OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC);
    Retrofit.Builder builder = new Retrofit.Builder()
            .baseUrl(blog.getUrl() + "/ghost/api/v3/admin/")
            .addConverterFactory(JacksonConverterFactory.create());
    httpClient.interceptors().clear();
    httpClient.addInterceptor(logging);
    httpClient.interceptors().add(chain -> {
        Request request = chain.request();
        Headers headers = request.headers().newBuilder().add("Authorization", "Ghost " + token).build();
        request = request.newBuilder().headers(headers).build();
        return chain.proceed(request);
    });
    Retrofit retrofit = builder.client(httpClient.build()).build();
    return retrofit.create(GhostBlogAdminApiService.class);
}

This is the retrofit GhostBlogAdminApiService interface I use :

public interface GhostBlogAdminApiService {
    @Headers({
            "Accept: application/json; charset=utf-8",
            "Content-Type: application/json; charset=utf-8",
    })
    @GET("site")
    Call<GhostResponse.Admin.Site> getSite();

    @Headers({
            "Accept: application/json; charset=utf-8",
            "Content-Type: application/json; charset=utf-8",
    })
    @POST("webooks/")
    Call<GhostResponse.Admin.Webhooks> createWebHook(
            @Body GhostAdminWebhook body
    );
}

I’ve helped my self with this SO question without success

Using the signed token, no matter if I can verify it or not, I keep having 404 response code when I hit POST on /ghost/api/v3/admin/webhooks.

I’ve tested the insecure ghost/api/v3/admin/site endpoint and it works great (so does all content endpoints), but site admin route is without authentication.

I multiple checked the called endpoint and there’s absolutely no mistake in it :

My Ghost blog is hosted behind a Traefik V2 HTTPS (let’s encrypt) reverse proxy and it worked so far for serving the website (perhaps not with the API?), please ask if you need this part of my configuration.

  • Why do I have 404 and the person in this question is having 401?
  • How did I fail?
  • How can I get going with the webhooks admin API?

Hi @Naz, sorry I used a wrong new account by mistake. Did you have the chance to have a look at my configuration? How can you explain webhooks endpoints to be totally wrong on their returned status code (I was expecting 200 or 401 but never 404…)

Do you have a clue why this is happening? I stopped giving it a try as I can’t explain this bug. Thanks a lot for any comment.

@Dimitri_Kopriwa this would need a good investigation and time to answer. Myself and the rest of the team has no capacity on their hands I’m afraid to dig through Java code. I think your best bet is reaching to other community members familiar with Java stack. Also, would try analyzing our official implementation in JS and maybe a long stretch but some inspiration can be taken from this .NET implementation.
Good luck!

@naz, thanks again for your reply. I already tried the Java community on freenode and stackoverflow and they weren’t able to help me further.

I understand the lack of human resources at the moment.

Perhaps later this could be a good opportunity to introduce a Java code snippet to perform admin API call?

Thanks for understanding. To the best of my knowledge, none of the team members are up to speed with Java. We’d probably rely on fully community version to add support for JVM platform just like it was done with .NET

By any chance, did you get some hint around this? I’ll keep watching it, at some point, one terminator will arrive and end this.

For me the API throws 404 when trying to log in with a non-existant username.