When you click a button/link it usually sends an html request, which is defined by Accept header:
def show
  puts request.headers["Accept"]
  # => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
  #     ^^^^^^^^^
  #     it's an html request. it's up to you (and rails) to respond with html
  #     or ignore it and render something else.
end
By default, Rails will render show.html.erb template and set Content-Type header to text/html.
Then you add show.json.jbuilder. Now you can ask for a json response instead of an html:
// with javascript
fetch("/models/1" , { headers: { Accept: "application/json" } })
  .then(response => response.json())
  .then(json => console.log(json)) // do something with the response
// and in controller you can see
// request.headers["Accept"] # => "application/json"
It's a json request so rails will render a json template.
Rails also gives you a way to render a particular response without using headers. Just add .json to the url. Accept: text/html in this case is ignored by rails and it will render a json template.
I don't remember any internal/external api concepts in rails, just a request and response.
If you need to add more logic in the controller to handle different types of requests use respond_to method:
def show
  respond_to do |format|
    format.html { render :different_template }
    format.json { render json: {error: "i'm not an api."} }
  end
end