Connecting SAP to the Google Gemini API: ABAP Practice Guide with SM59 and STRUST

This guide to SAP Gemini API ABAP integration explains step-by-step how SAP (ABAP) communicates securely with the Google Gemini API (Generative Language API).

We will also show you how to proceed from certificates in STRUST to the RFC destination in SM59 to productive HTTP POST in ABAP. You can also find more tips in our article What is SAP?.

Why SAP Gemini API ABAP Integration?

  • Text analysis, summaries, classification and prompt-based automation directly from ABAP.
  • In addition, the connection is easy via an API key and also via a standardized REST interface.
  • This makes the solution scalable from test operation to productive use.

Requirements

  • First, you need an SAP system with active HTTPS (ICM) and CommonCryptoLib.
  • You also need permissions for STRUST and SM59.
  • In addition, a valid Gemini API key is required.
  • You’ll also need an ABAP JSON utility, such as /UI2/CL_JSON.

Step 1: SM59 – Create HTTP destination for Google APIs

For the SAP Gemini API ABAP integration to work, you first create an HTTP destination in SM59 that SAP uses to address external web services or APIs.
For , generativelanguage.googleapis.com the basic configuration therefore looks like this:

Create a new HTTP connection

  1. Then click on Create.
  2. After that, select the connection type G – HTTP connection to external server.
  3. Then give a descriptive name (e.g. B. GOOGLE_GENAI_API or GOOGLE_GEMINI_API).

Technical settings

  • Host: generativelanguage.googleapis.com
  • Service No.: 443, because HTTPS uses this port
  • Path prefix: leave blank for now – the full path is set in the ABAP code
    (e.g. B. /v1/models/gemini-2.5-flash:generateContent).

Logon & Security

  • SNC: not required because HTTPS is used.
  • SSL: Select Active (SSL Client Standard)
    (or your dedicated SSL client identity, if stored in STRUST).

Registration & Security

Google APIs typically don’t maintain user/password data in the destination. Instead, authentication is done in the ABAP code – either via API key in the header or alternatively via OAuth 2.0 tokens (header
Authorization: Bearer ).

  • User/password: remains blank because authentication is done in the code.
  • SSL Client Certificate: Corresponds to the selection under Technical Settings.
    If there are several identities, select specifically if necessary.

Special options

  • HTTP proxy: maintain only if Internet access runs through it.
  • Timeouts: adjust as needed to catch slow responses.

Summary

  • Host: generativelanguage.googleapis.com
  • Service: 443
  • Path prefix: blank (path is set in code)
  • SSL: Active, SSL Client Standard
  • Login: empty (Auth in ABAP code)

Step 2: STRUST – Certificates for HTTPS access to Google APIs

In STRUST, you then maintain the SSL/TLS trust anchors so that your SAP system accepts the server certificates presented by Google.

Where to register?

First, switch to the SSL client node (default) in STRUST or alternatively to your specific SSL client identity. Then you will see on the right side of the
Certificate list : all certificates that are already familiar. Here, therefore, the root and – if available –
the intermediate certificates of Google’s issuing CA are also available.

What certificates are required?

Google usually uses chains of well-known CAs such as Google Trust Services (GTS), GlobalSign or DigiCert. In many systems, the important root certificates are already available because the operating system or CCL often provides them. However, if something is missing, add the chain manually.

Discovering and Exporting Certificates

  1. https://generativelanguage.googleapis.com in the browser.
  2. Click on the lock icon and view the certificate chain.
  3. Root and, if applicable, intermediate certificates (e.g. GTS Root R1, GTS CA 1O1) in Base64-X.509 format
    .cer( or .pem). Alternatively, download from the official pages of Google Trust Services.

Import into STRUST

  1. Open STRUST and select SSL client (default ).
  2. Then use the Import Certificate button and then select the saved files.
  3. Then add the certificates to the list and then back up the database so that the changes take effect.
  4. After importing, the entries should be visible, otherwise the import will fail.

Check the connection

Then run a Test Connection in SM59 at your destination. If the test fails, a
Root or Intermediate Certificate. Note, however: This test only checks the network and TLS handshake, while the later
authentication via API key or OAuth is not covered.

Summary

  • Location: SSL client (default) (or the SSL client PSE used).
  • Trusted root or intermediate certificates of the Google CAs (e.g. GTS Root R1, GTS CA 1O1) are also used as content.
  • As an action, you should first check the presence and then import and save missing certificates.

Next step in ABAP

As soon as the preparatory work is completed, the actual API communication (HTTP method, header, body, authentication) then takes place in the code,
for example with CL_HTTP_CLIENT or alternatively CL_REST_HTTP_CLIENT.

Step 3: SAP Gemini API ABAP Integration – POST Request to Gemini (Minimal Example)

Example endpoint (v1, model gemini-2.5-flash): /v1/models/gemini-2.5-flash:generateContent

REPORT z_ki_google_test.

DATA: lo_http    TYPE REF TO if_http_client,
      lv_body    TYPE string,
      lv_result  TYPE string,
      lv_api_key TYPE string VALUE '***PLACE_YOUR_API_KEY_SECURELY***'.

" 1) HTTP-Client aus SM59-Destination (G: HTTP)
cl_http_client=>create_by_destination(
  EXPORTING  destination = 'GEMINI'
  IMPORTING  client      = lo_http ).

" 2) Header setzen
lo_http->request->set_method( if_http_request=>co_request_method_post ).
lo_http->request->set_header_field( name = 'Content-Type'   value = 'application/json' ).
lo_http->request->set_header_field( name = 'x-goog-api-key' value = lv_api_key ).

" 3) Request-Pfad (Endpoint) – setze ihn NUR hier ODER als Präfix in SM59 (nicht beides!)
lo_http->request->set_header_field(
  name  = '~request_uri'
  value = '/v1/models/gemini-2.5-flash:generateContent' ).

" ---------- Request-JSON aufbauen (mit name_mappings für lowercase keys) ----------
TYPES: BEGIN OF ty_part_req,
         text TYPE string,
       END OF ty_part_req.
TYPES: ty_t_part_req TYPE STANDARD TABLE OF ty_part_req WITH EMPTY KEY.

TYPES: BEGIN OF ty_content_req,
         parts TYPE ty_t_part_req,
       END OF ty_content_req.
TYPES: ty_t_content_req TYPE STANDARD TABLE OF ty_content_req WITH EMPTY KEY.

TYPES: BEGIN OF ty_request,
         contents TYPE ty_t_content_req,
       END OF ty_request.

DATA ls_req TYPE ty_request.

DATA(lv_prompt_text) = |Erkläre KI in einem Satz.|.

APPEND VALUE ty_content_req(
         parts = VALUE #( ( text = lv_prompt_text ) ) )
       TO ls_req-contents.

DATA lt_req_map TYPE /ui2/cl_json=>name_mappings.
DATA lt_res_map TYPE /ui2/cl_json=>name_mappings.

" Request-Mapping (ABAP-Feldname -> JSON-Key, lowercase)
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'CONTENTS' json = 'contents' ) INTO TABLE lt_req_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'PARTS'    json = 'parts'    ) INTO TABLE lt_req_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'TEXT'     json = 'text'     ) INTO TABLE lt_req_map.

" Response-Mapping
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'CANDIDATES' json = 'candidates' ) INTO TABLE lt_res_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'CONTENT'    json = 'content'    ) INTO TABLE lt_res_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'PARTS'      json = 'parts'      ) INTO TABLE lt_res_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'TEXT'       json = 'text'       ) INTO TABLE lt_res_map.

lv_body = /ui2/cl_json=>serialize(
            data          = ls_req
            name_mappings = lt_req_map
            compress      = abap_true ).

" 4) Senden & Empfangen
lo_http->request->set_cdata( lv_body ).
lo_http->send( ).
lo_http->receive( ).

" 5) Antwort lesen (Status + Body)
DATA: lv_status TYPE i,
      lv_reason TYPE string.

lo_http->response->get_status(
  IMPORTING
    code   = lv_status
    reason = lv_reason ).

lv_result = lo_http->response->get_cdata( ).

" ---------- Response-JSON deserialisieren ----------
TYPES: BEGIN OF ty_part_res,
         text TYPE string,
       END OF ty_part_res.
TYPES: ty_t_part_res TYPE STANDARD TABLE OF ty_part_res WITH EMPTY KEY.

TYPES: BEGIN OF ty_content_res,
         parts TYPE ty_t_part_res,
       END OF ty_content_res.

TYPES: BEGIN OF ty_candidate,
         content TYPE ty_content_res,
       END OF ty_candidate.
TYPES: ty_t_candidate TYPE STANDARD TABLE OF ty_candidate WITH EMPTY KEY.

TYPES: BEGIN OF ty_response,
         candidates TYPE ty_t_candidate,
       END OF ty_response.

DATA ls_resp TYPE ty_response.

TRY.
  /ui2/cl_json=>deserialize(
    EXPORTING json          = lv_result
              name_mappings = lt_res_map
    CHANGING  data          = ls_resp ).

  DATA(lv_text) = ||.
  IF lines( ls_resp-candidates ) > 0
     AND lines( ls_resp-candidates[ 1 ]-content-parts ) > 0.
    lv_text = ls_resp-candidates[ 1 ]-content-parts[ 1 ]-text.
  ENDIF.

  "=== ALV-Vorbereitung ======================================================
  TYPES: BEGIN OF ty_company_info,
           field TYPE string,
           value TYPE string,
         END OF ty_company_info.

  DATA: lt_company_info TYPE STANDARD TABLE OF ty_company_info,
        ls_company_info TYPE ty_company_info.

  IF lv_status = 200 AND lv_text IS NOT INITIAL.

    " Antwort in Zeilen aufteilen und Feld: Wert extrahieren
    DATA(lt_lines) = VALUE stringtab( ).
    SPLIT lv_text AT cl_abap_char_utilities=>newline INTO TABLE lt_lines.

    LOOP AT lt_lines INTO DATA(lv_line).
      IF lv_line CS ':'.
        SPLIT lv_line AT ':' INTO ls_company_info-field ls_company_info-value.
        CONDENSE ls_company_info-field.
        SHIFT ls_company_info-value LEFT DELETING LEADING space.
      ELSE.
        ls_company_info-field = 'Text'.
        ls_company_info-value = lv_line.
      ENDIF.
      APPEND ls_company_info TO lt_company_info.
      CLEAR ls_company_info.
    ENDLOOP.

  ELSE.
    " Fehlerfall als ALV zeigen
    ls_company_info-field = |HTTP|.
    ls_company_info-value = |{ lv_status } { lv_reason }|.
    APPEND ls_company_info TO lt_company_info.

    CLEAR ls_company_info.
    ls_company_info-field = |Fehlermeldung|.
    ls_company_info-value = lv_result.
    APPEND ls_company_info TO lt_company_info.
  ENDIF.

  "=== ALV-Ausgabe ===========================================================
  DATA: lo_alv     TYPE REF TO cl_salv_table,
        lo_columns TYPE REF TO cl_salv_columns_table,
        lo_column  TYPE REF TO cl_salv_column_table.

  cl_salv_table=>factory(
    IMPORTING r_salv_table = lo_alv
    CHANGING  t_table      = lt_company_info ).

  lo_columns = lo_alv->get_columns( ).
  lo_columns->set_optimize( abap_true ).

  lo_column ?= lo_columns->get_column( 'FIELD' ).
  lo_column->set_short_text(  'Kategorie' ).
  lo_column->set_medium_text( 'Kategorie' ).
  lo_column->set_long_text(   'Informationskategorie' ).

  lo_column ?= lo_columns->get_column( 'VALUE' ).
  lo_column->set_short_text(  'Inhalt' ).
  lo_column->set_medium_text( 'Beschreibung' ).
  lo_column->set_long_text(   'Detail' ).

  lo_alv->display( ).

CATCH cx_root INTO DATA(lx).
  " Fallback: Fehler in einfacher Liste ausgeben
  WRITE: / 'JSON-/ALV-Fehler:', lx->get_text( ).
  WRITE: / 'Raw-Response:', / lv_result.
ENDTRY.

lo_http->close( ).

Best Practices for SAP Gemini API ABAP Integration

  • Do not hardcode the API key, but store it securely (secure store, variables, SICF handlers).
  • You should also include timeouts and retries so that error states are logged clearly.
  • Also, keep prompts short and clear so that the answers remain consistent.
  • Model choice: gemini-2.5-flash for cost/speed, gemini-2.5-pro for quality.

Troubleshooting

  • 401 Unauthorized: This is either x-goog-api-key missing or incorrect.
  • 400 Bad Request: Check JSON structure (contents → parts → text).
  • 404 Not Found: Therefore, you should check the model name and URI.
  • SSL handshake errors occur because the CA chain in STRUST is incomplete.
  • Proxy/Firewall: Therefore, you should check both SM59 proxy and network shares.

Checklist

  • Imported CA chain into STRUST so that TLS works
  • In addition, host, port 443 and SSL are active in SM59
  • API key securely stored so that the header is also set correctly
  • Also, the endpoint is correct: /v1/models/gemini-2.5-flash:generateContent
  • There are also timeouts, retries, and logging

With this checklist, you will complete the SAP Gemini API ABAP integration. It also ensures that your system works reliably.

Thilo Kiefer

Thilo Kiefer

CEO and Product Manager SAP Add-Ons

For me, real innovation arises where technology serves people and not the other way around.